diff options
77 files changed, 45494 insertions, 27467 deletions
diff --git a/exception_lists/copyright b/exception_lists/copyright index 01169dbb04..2e4281fda5 100644 --- a/exception_lists/copyright +++ b/exception_lists/copyright @@ -30,6 +30,11 @@ exception_lists/closed-bins exception_lists/copyright exception_lists/cstyle exception_lists/hdrchk +usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.c +usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.h +usr/src/cmd/cmd-inet/usr.bin/dns-sd/dns-sd.c +usr/src/cmd/cmd-inet/usr.lib/mdnsd/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.lib/mdnsd/*.[ch] usr/src/cmd/dtrace/test/tst/common/*/*.out usr/src/cmd/krb5/kadmin/cli/kadmin_ct.c usr/src/cmd/krb5/kadmin/cli/kadmin.h @@ -340,6 +345,11 @@ usr/src/lib/krb5/ss/mit-sipb-copyright.h usr/src/lib/krb5/ss/options.c usr/src/lib/krb5/ss/std_rqs.c usr/src/lib/krb5/ss/utils.c +usr/src/lib/libdns_sd/THIRDPARTYLICENSE +usr/src/lib/libdns_sd/common/*.[ch] +usr/src/lib/libdns_sd/java/common/JNISupport.c +usr/src/lib/libdns_sd/java/com/apple/dnssd/*.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/* usr/src/lib/librstp/common/*.[ch] usr/src/lib/librstp/common/[CRT]* # these have copyrights that the nit checker doesn't grok diff --git a/exception_lists/cstyle b/exception_lists/cstyle index 4a10306206..e0414c63ce 100644 --- a/exception_lists/cstyle +++ b/exception_lists/cstyle @@ -1,3 +1,21 @@ +usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.[ch] +usr/src/cmd/cmd-inet/usr.bin/dns-sd/dns-sd.c +usr/src/cmd/cmd-inet/usr.lib/mdnsd/CryptoAlg.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c +usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/nsec.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/anonymous.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/dnssec.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.[ch] +usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.[ch] usr/src/cmd/krb5/kadmin/cli/kadmin_ct.c usr/src/cmd/krb5/kadmin/cli/kadmin.c usr/src/cmd/krb5/kadmin/cli/kadmin.h @@ -670,6 +688,12 @@ usr/src/lib/krb5/ss/ss_internal.h usr/src/lib/krb5/ss/ss.h usr/src/lib/krb5/ss/std_rqs.c usr/src/lib/krb5/ss/utils.c +usr/src/lib/libdns_sd/common/dnssd_clientlib.c +usr/src/lib/libdns_sd/common/dnssd_clientstub.c +usr/src/lib/libdns_sd/common/dnssd_ipc.c +usr/src/lib/libdns_sd/common/dnssd_ipc.h +usr/src/lib/libdns_sd/common/dns_sd.h +usr/src/lib/libdns_sd/java/common/JNISupport.c usr/src/lib/libgss/g_glue.c usr/src/lib/libresolv2/common usr/src/lib/librstp/common/base.h diff --git a/exception_lists/hdrchk b/exception_lists/hdrchk index 1aa0db27dd..476eab8a02 100644 --- a/exception_lists/hdrchk +++ b/exception_lists/hdrchk @@ -1,3 +1,17 @@ +usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/CryptoAlg.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/anonymous.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/dnssec.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/nsec.h +usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h usr/src/cmd/krb5/kadmin/cli/kadmin.h usr/src/cmd/krb5/kadmin/dbutil/import_err.h usr/src/cmd/krb5/kadmin/dbutil/kdb5_util.h @@ -179,6 +193,8 @@ usr/src/lib/krb5/ss/mit-sipb-copyright.h usr/src/lib/krb5/ss/ss_internal.h usr/src/lib/krb5/ss/ss.h usr/src/lib/libc/port/locale/utils.h +usr/src/lib/libdns_sd/common/dns_sd.h +usr/src/lib/libdns_sd/common/dnssd_ipc.h usr/src/lib/librstp/common/base.h usr/src/lib/librstp/common/choose.h usr/src/lib/librstp/common/edge.h diff --git a/exception_lists/jstyle b/exception_lists/jstyle new file mode 100644 index 0000000000..32e6665a9f --- /dev/null +++ b/exception_lists/jstyle @@ -0,0 +1,35 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Toomas Soome <tsoome@me.com> +# + +usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java +usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java diff --git a/exception_lists/keywords b/exception_lists/keywords index 2ac3a477c0..632d4e55ff 100644 --- a/exception_lists/keywords +++ b/exception_lists/keywords @@ -21,9 +21,11 @@ # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2013 by Delphix. All rights reserved. +# Copyright 2016 Toomas Soome <tsoome@me.com> # syntax: glob +usr/src/cmd/cmd-inet/usr.bin/dns-sd/dns-sd.c usr/src/cmd/localedef/data/zh_CN.UTF-8.src usr/src/cmd/localedef/data/zh_HK.UTF-8.src usr/src/cmd/localedef/data/zh_MO.UTF-8.src @@ -12005,6 +12005,7 @@ f usr/share/man/man1m/mail.local.1m 0444 root bin f usr/share/man/man1m/mailwrapper.1m 0444 root bin f usr/share/man/man1m/makedbm.1m 0444 root bin f usr/share/man/man1m/makemap.1m 0444 root bin +f usr/share/man/man1m/mdnsd.1m 0444 root bin f usr/share/man/man1m/mkdevalloc.1m 0444 root bin f usr/share/man/man1m/mkdevmaps.1m 0444 root bin f usr/share/man/man1m/mkfifo.1m 0444 root bin diff --git a/usr/src/cmd/cmd-inet/usr.bin/Makefile b/usr/src/cmd/cmd-inet/usr.bin/Makefile index 5ad5213eb6..68ecf76b36 100644 --- a/usr/src/cmd/cmd-inet/usr.bin/Makefile +++ b/usr/src/cmd/cmd-inet/usr.bin/Makefile @@ -23,15 +23,16 @@ # Use is subject to license terms. # # Copyright 2011 Nexenta Systems, Inc. All rights reserved. +# Copyright 2016 Toomas Soome <tsoome@me.com> # -PROG= dns-sd finger rdate ruptime rwho whois +PROG= finger rdate ruptime rwho whois SUIDPROG= rcp rlogin rsh ALL= $(PROG) $(SUIDPROG) SRCS= $(ALL:%=%.c) KCMDPROGS= rcp rlogin rsh -SUBDIRS= chat ftp nc nca netstat \ +SUBDIRS= chat dns-sd ftp nc nca netstat \ pppd pppdump pppstats rdist talk telnet tftp SUBDIR1= talk MSGSUBDIRS= nca talk @@ -78,12 +79,10 @@ CERRWARN += -_gcc=-Wno-unused-function # 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 deleted file mode 100644 index aa123c921c..0000000000 --- a/usr/src/cmd/cmd-inet/usr.bin/dns-sd.c +++ /dev/null @@ -1,805 +0,0 @@ -/* -*- 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.bin/dns-sd/ClientCommon.c b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.c new file mode 100644 index 0000000000..68f354ccac --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.c @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <ctype.h> +#include <stdio.h> // For stdout, stderr + +#include "ClientCommon.h" + +const char *GetNextLabel(const char *cstr, char label[64]) +{ + char *ptr = label; + while (*cstr && *cstr != '.') // While we have characters in the label... + { + char c = *cstr++; + if (c == '\\' && *cstr) // If we have a backslash, and it's not the last character of the string + { + c = *cstr++; + if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1])) + { + int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal + int v1 = cstr[ 0] - '0'; + int v2 = cstr[ 1] - '0'; + int val = v0 * 100 + v1 * 10 + v2; + // If valid three-digit decimal value, use it + // Note that although ascii nuls are possible in DNS labels + // we're building a C string here so we have no way to represent that + if (val == 0) val = '-'; + if (val <= 255) { c = (char)val; cstr += 2; } + } + } + *ptr++ = c; + if (ptr >= label+64) { label[63] = 0; return(NULL); } // Illegal label more than 63 bytes + } + *ptr = 0; // Null-terminate label text + if (ptr == label) return(NULL); // Illegal empty label + if (*cstr) cstr++; // Skip over the trailing dot (if present) + return(cstr); +} diff --git a/usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.h b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.h new file mode 100644 index 0000000000..afe5b7a501 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/ClientCommon.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +extern const char *GetNextLabel(const char *cstr, char label[64]); diff --git a/usr/src/cmd/cmd-inet/usr.bin/dns-sd/Makefile b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/Makefile new file mode 100644 index 0000000000..cc3ab285c0 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/Makefile @@ -0,0 +1,47 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Toomas Soome <tsoome@me.com> +# + +# cmd/cmd-inet/usr.bin/dns-sd/Makefile + +PROG= dns-sd + +include ../../../Makefile.cmd +include ../../Makefile.cmd-inet + +OBJS= ClientCommon.o dns-sd.o +SRCS= ClientCommon.c dns-sd.c + +CFLAGS += $(C99_ENABLE) +LDLIBS += -lsocket -ldns_sd + +.KEEP_STATE: + +all: $(PROG) + +ROOTPROG= $(PROG:%=$(ROOTBIN)/%) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +install: all $(ROOTPROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +include ../../../Makefile.targ + diff --git a/usr/src/cmd/cmd-inet/usr.bin/dns-sd/dns-sd.c b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/dns-sd.c new file mode 100644 index 0000000000..9acf41e581 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.bin/dns-sd/dns-sd.c @@ -0,0 +1,1820 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2013 Apple Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + To build this tool, copy and paste the following into a command line: + + OS X: + gcc dns-sd.c -o dns-sd + + POSIX systems: + gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd + + Windows: + cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib + (may require that you run a Visual Studio script such as vsvars32.bat first) + */ + +// For testing changes to dnssd_clientstub.c, uncomment this line and the code will be compiled +// with an embedded copy of the client stub instead of linking the system library version at runtime. +// This also useful to work around link errors when you're working on an older version of Mac OS X, +// and trying to build a newer version of the "dns-sd" command which uses new API entry points that +// aren't in the system's /usr/lib/libSystem.dylib. +//#define TEST_NEW_CLIENTSTUB 1 + +// When building mDNSResponder for Mac OS X 10.4 and earlier, /usr/lib/libSystem.dylib is built using its own private +// copy of dnssd_clientstub.c, which is old and doesn't have all the entry points defined in the latest version, so +// when we're building dns-sd.c on Mac OS X 10.4 or earlier, we automatically set TEST_NEW_CLIENTSTUB so that we'll +// embed a copy of the latest dnssd_clientstub.c instead of trying to link to the incomplete version in libSystem.dylib +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1040 +#define TEST_NEW_CLIENTSTUB 1 +#endif + +#include <ctype.h> +#include <stdio.h> // For stdout, stderr +#include <stdlib.h> // For exit() +#include <string.h> // For strlen(), strcpy() +#include <errno.h> // For errno, EINTR +#include <time.h> +#include <sys/types.h> // For u_char + +#ifdef _WIN32 + #include <winsock2.h> + #include <ws2tcpip.h> + #include <Iphlpapi.h> + #include <process.h> +typedef int pid_t; + #define getpid _getpid + #define strcasecmp _stricmp + #define snprintf _snprintf +static const char kFilePathSep = '\\'; + #ifndef HeapEnableTerminationOnCorruption + # define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1 + #endif + #if !defined(IFNAMSIZ) + #define IFNAMSIZ 16 + #endif + #define if_nametoindex if_nametoindex_win + #define if_indextoname if_indextoname_win + +typedef PCHAR (WINAPI * if_indextoname_funcptr_t)(ULONG index, PCHAR name); +typedef ULONG (WINAPI * if_nametoindex_funcptr_t)(PCSTR name); + +unsigned if_nametoindex_win(const char *ifname) +{ + HMODULE library; + unsigned index = 0; + + // Try and load the IP helper library dll + if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL ) + { + if_nametoindex_funcptr_t if_nametoindex_funcptr; + + // On Vista and above there is a Posix like implementation of if_nametoindex + if ((if_nametoindex_funcptr = (if_nametoindex_funcptr_t) GetProcAddress(library, "if_nametoindex")) != NULL ) + { + index = if_nametoindex_funcptr(ifname); + } + + FreeLibrary(library); + } + + return index; +} + +char * if_indextoname_win( unsigned ifindex, char *ifname) +{ + HMODULE library; + char * name = NULL; + + // Try and load the IP helper library dll + if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL ) + { + if_indextoname_funcptr_t if_indextoname_funcptr; + + // On Vista and above there is a Posix like implementation of if_indextoname + if ((if_indextoname_funcptr = (if_indextoname_funcptr_t) GetProcAddress(library, "if_indextoname")) != NULL ) + { + name = if_indextoname_funcptr(ifindex, ifname); + } + + FreeLibrary(library); + } + + return name; +} + +static size_t _sa_len(const struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) return (sizeof(struct sockaddr_in)); + else if (addr->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6)); + else return (sizeof(struct sockaddr)); +} + +# define SA_LEN(addr) (_sa_len(addr)) + +#else + #include <unistd.h> // For getopt() and optind + #include <netdb.h> // For getaddrinfo() + #include <sys/time.h> // For struct timeval + #include <sys/socket.h> // For AF_INET + #include <netinet/in.h> // For struct sockaddr_in() + #include <arpa/inet.h> // For inet_addr() + #include <net/if.h> // For if_nametoindex() +static const char kFilePathSep = '/'; +// #ifndef NOT_HAVE_SA_LEN +// #define SA_LEN(addr) ((addr)->sa_len) +// #else + #define SA_LEN(addr) (((addr)->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) +// #endif +#endif + +#if (TEST_NEW_CLIENTSTUB && !defined(__APPLE_API_PRIVATE)) +#define __APPLE_API_PRIVATE 1 +#endif + +// DNSServiceSetDispatchQueue is not supported on 10.6 & prior +#if !TEST_NEW_CLIENTSTUB && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ % 10) <= 1060) +#undef _DNS_SD_LIBDISPATCH +#endif +#include "dns_sd.h" +#include "ClientCommon.h" + +#if TEST_NEW_CLIENTSTUB +#include "../mDNSShared/dnssd_ipc.c" +#include "../mDNSShared/dnssd_clientlib.c" +#include "../mDNSShared/dnssd_clientstub.c" +#endif + +#if _DNS_SD_LIBDISPATCH +#include <dispatch/private.h> +#endif + +// The "+0" is to cope with the case where _DNS_SD_H is defined but empty (e.g. on Mac OS X 10.4 and earlier) +#if _DNS_SD_H+0 >= 116 +#define HAS_NAT_PMP_API 1 +#define HAS_ADDRINFO_API 1 +#else +#define kDNSServiceFlagsReturnIntermediates 0 +#endif + +//************************************************************************************************************* +// Globals + +#define DS_FIXED_SIZE 4 +typedef struct +{ + unsigned short keyTag; + unsigned char alg; + unsigned char digestType; + unsigned char *digest; +} rdataDS; + +#define DNSKEY_FIXED_SIZE 4 +typedef struct +{ + unsigned short flags; + unsigned char proto; + unsigned char alg; + unsigned char *data; +} rdataDNSKey; + +//size of rdataRRSIG excluding signerName and signature (which are variable fields) +#define RRSIG_FIXED_SIZE 18 +typedef struct +{ + unsigned short typeCovered; + unsigned char alg; + unsigned char labels; + unsigned int origTTL; + unsigned int sigExpireTime; + unsigned int sigInceptTime; + unsigned short keyTag; + char signerName[256]; + //unsigned char *signature +} rdataRRSig; + +#define RR_TYPE_SIZE 16 + +typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; + +static int operation; +static uint32_t opinterface = kDNSServiceInterfaceIndexAny; +static DNSServiceRef client = NULL; +static DNSServiceRef client_pa = NULL; // DNSServiceRef for RegisterProxyAddressRecord +static DNSServiceRef sc1, sc2, sc3; // DNSServiceRefs for kDNSServiceFlagsShareConnection testing + +static int num_printed; +static char addtest = 0; +static DNSRecordRef record = NULL; +static char myhinfoW[14] = "\002PC\012Windows XP"; +static char myhinfoX[ 9] = "\003Mac\004OS X"; +static char updatetest[3] = "\002AA"; +static char bigNULL[8192]; // 8K is maximum rdata we support + +#if _DNS_SD_LIBDISPATCH +dispatch_queue_t main_queue; +dispatch_source_t timer_source; +#endif + +// Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this +#define LONG_TIME 100000000 + +static volatile int stopNow = 0; +static volatile int timeOut = LONG_TIME; + +#if _DNS_SD_LIBDISPATCH +#define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) \ + if (main_queue && (E) == kDNSServiceErr_ServiceNotRunning) { fprintf(stderr, "Error code %d\n", (E)); exit(0); } +#else +#define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) +#endif + +//************************************************************************************************************* +// Supporting Utility Functions +static uint16_t GetRRClass(const char *s) +{ + if (!strcasecmp(s, "IN")) + return kDNSServiceClass_IN; + else + return(atoi(s)); +} + +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, "dnskey" )) return(kDNSServiceType_DNSKEY); + else if (!strcasecmp(s, "ds" )) return(kDNSServiceType_DS); + else if (!strcasecmp(s, "rrsig" )) return(kDNSServiceType_RRSIG); + else if (!strcasecmp(s, "nsec" )) return(kDNSServiceType_NSEC); + else if (!strcasecmp(s, "ANY" )) return(kDNSServiceType_ANY); + else return(atoi(s)); +} + +static char *DNSTypeName(unsigned short rr_type) +{ + switch (rr_type) + { + case kDNSServiceType_A: return("Addr"); + case kDNSServiceType_NS: return("NS"); + case kDNSServiceType_MX: return("MX"); + case kDNSServiceType_CNAME: return("CNAME"); + case kDNSServiceType_SOA: return("SOA"); + case kDNSServiceType_PTR: return("PTR"); + case kDNSServiceType_AAAA: return("AAAA"); + case kDNSServiceType_NSEC: return("NSEC"); + case kDNSServiceType_TSIG: return("TSIG"); + case kDNSServiceType_RRSIG: return("RRSIG"); + case kDNSServiceType_DNSKEY: return("DNSKEY"); + case kDNSServiceType_DS: return("DS"); + default: + { + static char buffer[RR_TYPE_SIZE]; + snprintf(buffer, sizeof(buffer), "TYPE%d", rr_type); + return(buffer); + } + } +} + +static unsigned short swap16(unsigned short x) +{ + unsigned char *ptr = (unsigned char *)&x; + return (unsigned short)((unsigned short)ptr[0] << 8 | ptr[1]); +} + +static unsigned int swap32(unsigned int x) +{ + unsigned char *ptr = (unsigned char *)&x; + return (unsigned int)((unsigned int)ptr[0] << 24 | (unsigned int)ptr[1] << 16 | (unsigned int)ptr[2] << 8 | ptr[3]); +} +static unsigned int keytag(unsigned char *key, unsigned int keysize) +{ + unsigned long ac; + unsigned int i; + + for (ac = 0, i = 0; i < keysize; ++i) + ac += (i & 1) ? key[i] : key[i] << 8; + ac += (ac >> 16) & 0xFFFF; + return ac & 0xFFFF; +} + +static void base64Encode(char *buffer, int buflen, void *rdata, unsigned int rdlen) +{ +#if _DNS_SD_LIBDISPATCH + const void *result = NULL; + size_t size; + dispatch_data_t src_data = NULL, dest_data = NULL, null_str = NULL, data = NULL, map = NULL; + + src_data = dispatch_data_create(rdata, rdlen, dispatch_get_global_queue(0, 0), ^{}); + if (!src_data) + goto done; + + dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE, DISPATCH_DATA_FORMAT_TYPE_BASE64); + if (!dest_data) + goto done; + + null_str = dispatch_data_create("", 1, dispatch_get_global_queue(0, 0), ^{}); + if (!null_str) + goto done; + + data = dispatch_data_create_concat(dest_data, null_str); + if (!data) + goto done; + + map = dispatch_data_create_map(data, &result, &size); + if (!map) + goto done; + + snprintf(buffer, buflen, " %s", (char *)result); + +done: + if (src_data) dispatch_release(src_data); + if (dest_data) dispatch_release(dest_data); + if (data) dispatch_release(data); + if (null_str) dispatch_release(null_str); + if (map) dispatch_release(map); + return; +#else //_DNS_SD_LIBDISPATCH + snprintf(buffer, buflen, " %s", "."); + return; +#endif //_DNS_SD_LIBDISPATCH +} + +#if HAS_NAT_PMP_API | HAS_ADDRINFO_API +static DNSServiceProtocol GetProtocol(const char *s) +{ + if (!strcasecmp(s, "v4" )) return(kDNSServiceProtocol_IPv4); + else if (!strcasecmp(s, "v6" )) return(kDNSServiceProtocol_IPv6); + else if (!strcasecmp(s, "v4v6" )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); + else if (!strcasecmp(s, "v6v4" )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); + else if (!strcasecmp(s, "udp" )) return(kDNSServiceProtocol_UDP); + else if (!strcasecmp(s, "tcp" )) return(kDNSServiceProtocol_TCP); + else if (!strcasecmp(s, "udptcp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); + else if (!strcasecmp(s, "tcpudp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); + else return(atoi(s)); +} +#endif + + +//************************************************************************************************************* +// Sample callback functions for each of the operation types + +static void printtimestamp(void) +{ + struct tm tm; + int ms; + static char date[16]; + static char new_date[16]; +#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 + strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm); + if (strncmp(date, new_date, sizeof(new_date))) + { + printf("DATE: ---%s---\n", new_date); //display date only if it has changed + strncpy(date, new_date, sizeof(date)); + } + printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); +} + +// formating time to RFC 4034 format +static void FormatTime(unsigned long te, unsigned char *buf, int bufsize) +{ + struct tm tmTime; +#ifdef _WIN32 + __time32_t t = (__time32_t) te; + _gmtime32_s(&tmTime, &t); +#else + // Time since epoch : strftime takes "tm". Convert seconds to "tm" using + // gmtime_r first and then use strftime + time_t t = (time_t)te; + gmtime_r(&t, &tmTime); +#endif + strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime); +} + +static void print_usage(const char *arg0, int print_all) +{ + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", arg0); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", arg0); + fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", arg0); + fprintf(stderr, "%s -B <Type> <Domain> (Browse for services instances)\n", arg0); + fprintf(stderr, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", arg0); + fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...] (Proxy)\n", arg0); + fprintf(stderr, "%s -q <name> <rrtype> <rrclass> (Generic query for any record type)\n", arg0); + fprintf(stderr, "%s -D <name> <rrtype> <rrclass>(Validate query for any record type with DNSSEC)\n", arg0); + fprintf(stderr, "%s -Z <Type> <Domain> (Output results in Zone File format)\n", arg0); +#if HAS_ADDRINFO_API + fprintf(stderr, "%s -G v4/v6/v4v6 <name> (Get address information for hostname)\n", arg0); + fprintf(stderr, "%s -g v4/v6/v4v6 <name> (Validate address info for hostname with DNSSEC)\n", arg0); +#endif + fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", arg0); + + if (print_all) //Print all available options for dns-sd tool + { + fprintf(stderr, "%s -C <FQDN> <rrtype> <rrclass> (Query; reconfirming each result)\n", arg0); +#if HAS_NAT_PMP_API + fprintf(stderr, "%s -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL> (NAT Port Mapping)\n", arg0); +#endif + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", arg0); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", arg0); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", arg0); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", arg0); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", arg0); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", arg0); + fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", arg0); + fprintf(stderr, "%s -i <Interface> (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0); + fprintf(stderr, "%s -lo (Run dns-sd cmd using local only interface)\n", arg0); + fprintf(stderr, "%s -p2p (Use kDNSServiceInterfaceIndexP2P)\n", arg0); + fprintf(stderr, "%s -includep2p (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0); + fprintf(stderr, "%s -includeAWDL (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0); + fprintf(stderr, "%s -optional (Set kDNSServiceFlagsValidateOptional flag)\n", arg0); + fprintf(stderr, "%s -tc (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0); + fprintf(stderr, "%s -unicastResponse (Set kDNSServiceFlagsUnicastResponse flag)\n", arg0); + fprintf(stderr, "%s -t1 (Set kDNSServiceFlagsThresholdOne flag)\n", arg0); + fprintf(stderr, "%s -tFinder (Set kDNSServiceFlagsThresholdFinder flag)\n", arg0); + fprintf(stderr, "%s -timeout (Set kDNSServiceFlagsTimeout flag)\n", arg0); + } +} + +#define DomainMsg(X) (((X) &kDNSServiceFlagsDefault) ? "(Default)" : \ + ((X) &kDNSServiceFlagsAdd) ? "Added" : "Removed") + +#define MAX_LABELS 128 + +static void DNSSD_API enum_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) +{ + DNSServiceFlags partialflags = flags & ~(kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault); + int labels = 0, depth = 0, i, initial = 0; + char text[64]; + const char *label[MAX_LABELS]; + + (void)sdref; // Unused + (void)ifIndex; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + // 1. Print the header + if (num_printed++ == 0) printf("Timestamp Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing"); + printtimestamp(); + if (errorCode) + printf("Error code %d\n", errorCode); + else if (!*replyDomain) + printf("Error: No reply domain\n"); + else + { + printf("%-10s", DomainMsg(flags)); + printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : ""); + if (partialflags) printf("Flags: %4X ", partialflags); + else printf(" "); + + // 2. Count the labels + while (replyDomain && *replyDomain && labels < MAX_LABELS) + { + label[labels++] = replyDomain; + replyDomain = GetNextLabel(replyDomain, text); + } + + // 3. Decide if we're going to clump the last two or three labels (e.g. "apple.com", or "nicta.com.au") + if (labels >= 3 && replyDomain - label[labels-1] <= 3 && label[labels-1] - label[labels-2] <= 4) initial = 3; + else if (labels >= 2 && replyDomain - label[labels-1] <= 4) initial = 2; + else initial = 1; + labels -= initial; + + // 4. Print the initial one-, two- or three-label clump + for (i=0; i<initial; i++) + { + GetNextLabel(label[labels+i], text); + if (i>0) printf("."); + printf("%s", text); + } + printf("\n"); + + // 5. Print the remainder of the hierarchy + for (depth=0; depth<labels; depth++) + { + printf(" "); + for (i=0; i<=depth; i++) printf("- "); + GetNextLabel(label[labels-1-depth], text); + printf("> %s\n", text); + } + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} + +static int CopyLabels(char *dst, const char *lim, const char **srcp, int labels) +{ + const char *src = *srcp; + while (*src != '.' || --labels > 0) + { + if (*src == '\\') *dst++ = *src++; // Make sure "\." doesn't confuse us + if (!*src || dst >= lim) return -1; + *dst++ = *src++; + if (!*src || dst >= lim) return -1; + } + *dst++ = 0; + *srcp = src + 1; // skip over final dot + return 0; +} + +static void DNSSD_API zonedata_resolve(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, + const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txt, void *context) +{ + union { uint16_t s; u_char b[2]; } port = { opaqueport }; + uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; + + const char *p = fullname; + char n[kDNSServiceMaxDomainName]; + char t[kDNSServiceMaxDomainName]; + + const unsigned char *max = txt + txtLen; + + (void)sdref; // Unused + (void)ifIndex; // Unused + (void)context; // Unused + + //if (!(flags & kDNSServiceFlagsAdd)) return; + if (errorCode) { printf("Error code %d\n", errorCode); return; } + + if (CopyLabels(n, n + kDNSServiceMaxDomainName, &p, 3)) return; // Fetch name+type + p = fullname; + if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 1)) return; // Skip first label + if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 2)) return; // Fetch next two labels (service type) + + if (num_printed++ == 0) + { + printf("\n"); + printf("; To direct clients to browse a different domain, substitute that domain in place of '@'\n"); + printf("%-47s PTR %s\n", "lb._dns-sd._udp", "@"); + printf("\n"); + printf("; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.\n"); + printf("; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local\n"); + printf("; names with the correct fully-qualified (unicast) domain name of the target host offering the service.\n"); + } + + printf("\n"); + printf("%-47s PTR %s\n", t, n); + printf("%-47s SRV 0 0 %d %s ; Replace with unicast FQDN of target host\n", n, PortAsNumber, hosttarget); + printf("%-47s TXT ", n); + + while (txt < max) + { + const unsigned char *const end = txt + 1 + txt[0]; + txt++; // Skip over length byte + printf(" \""); + while (txt<end) + { + if (*txt == '\\' || *txt == '\"') printf("\\"); + printf("%c", *txt++); + } + printf("\""); + } + printf("\n"); + + DNSServiceRefDeallocate(sdref); + free(context); + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} + +static void DNSSD_API zonedata_browse(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, + const char *replyName, const char *replyType, const char *replyDomain, void *context) +{ + DNSServiceRef *newref; + + (void)sdref; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (!(flags & kDNSServiceFlagsAdd)) return; + if (errorCode) { printf("Error code %d\n", errorCode); return; } + + newref = malloc(sizeof(*newref)); + *newref = client; + DNSServiceResolve(newref, kDNSServiceFlagsShareConnection, ifIndex, replyName, replyType, replyDomain, zonedata_resolve, newref); +} + +static void DNSSD_API browse_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, + const char *replyName, const char *replyType, const char *replyDomain, void *context) +{ + char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + (void)sdref; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-20s %-20s %s\n", "Domain", "Service Type", "Instance Name"); + printtimestamp(); + if (errorCode) + printf("Error code %d\n", errorCode); + else + printf("%s %8X %3d %-20s %-20s %s\n", + op, flags, ifIndex, replyDomain, replyType, replyName); + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + + // To test selective cancellation of operations of shared sockets, + // cancel the current operation when we've got a multiple of five results + //if (operation == 'S' && num_printed % 5 == 0) DNSServiceRefDeallocate(sdref); +} + +static void ShowTXTRecord(uint16_t txtLen, const unsigned char *txtRecord) +{ + const unsigned char *ptr = txtRecord; + const unsigned char *max = txtRecord + txtLen; + while (ptr < max) + { + const unsigned char *const end = ptr + 1 + ptr[0]; + if (end > max) { printf("<< invalid data >>"); break; } + if (++ptr < end) printf(" "); // As long as string is non-empty, begin with a space + while (ptr<end) + { + // We'd like the output to be shell-friendly, so that it can be copied and pasted unchanged into a "dns-sd -R" command. + // However, this is trickier than it seems. Enclosing a string in double quotes doesn't necessarily make it + // shell-safe, because shells still expand variables like $foo even when they appear inside quoted strings. + // Enclosing a string in single quotes is better, but when using single quotes even backslash escapes are ignored, + // meaning there's simply no way to represent a single quote (or apostrophe) inside a single-quoted string. + // The only remaining solution is not to surround the string with quotes at all, but instead to use backslash + // escapes to encode spaces and all other known shell metacharacters. + // (If we've missed any known shell metacharacters, please let us know.) + // In addition, non-printing ascii codes (0-31) are displayed as \xHH, using a two-digit hex value. + // Because '\' is itself a shell metacharacter (the shell escape character), it has to be escaped as "\\" to survive + // the round-trip to the shell and back. This means that a single '\' is represented here as EIGHT backslashes: + // The C compiler eats half of them, resulting in four appearing in the output. + // The shell parses those four as a pair of "\\" sequences, passing two backslashes to the "dns-sd -R" command. + // The "dns-sd -R" command interprets this single "\\" pair as an escaped literal backslash. Sigh. + if (strchr(" &;`'\"|*?~<>^()[]{}$", *ptr)) printf("\\"); + if (*ptr == '\\') printf("\\\\\\\\"); + else if (*ptr >= ' ' ) printf("%c", *ptr); + else printf("\\\\x%02X", *ptr); + ptr++; + } + } +} + +static void DNSSD_API resolve_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, + const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txtRecord, void *context) +{ + union { uint16_t s; u_char b[2]; } port = { opaqueport }; + uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; + + (void)sdref; // Unused + (void)ifIndex; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (errorCode) + printf("Error code %d\n", errorCode); + else + { + printtimestamp(); + printf("%s can be reached at %s:%u (interface %d)", fullname, hosttarget, PortAsNumber, ifIndex); + if (flags) printf(" Flags: %X", flags); + // Don't show degenerate TXT records containing nothing but a single empty string + if (txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); } + printf("\n"); + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} + +static void myTimerCallBack(void) +{ + DNSServiceErrorType err = kDNSServiceErr_Unknown; + + switch (operation) + { + case 'A': + { + switch (addtest) + { + case 0: printf("Adding Test HINFO record\n"); + err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_HINFO, sizeof(myhinfoW), &myhinfoW[0], 0); + addtest = 1; + break; + case 1: printf("Updating Test HINFO record\n"); + err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 0); + addtest = 2; + break; + case 2: printf("Removing Test HINFO record\n"); + err = DNSServiceRemoveRecord(client, record, 0); + addtest = 0; + break; + } + } + break; + + case 'U': + { + if (updatetest[1] != 'Z') updatetest[1]++; + else updatetest[1] = 'A'; + updatetest[0] = 3 - updatetest[0]; + updatetest[2] = updatetest[1]; + printtimestamp(); + printf("Updating Test TXT record to %c\n", updatetest[1]); + err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 0); + } + break; + + case 'N': + { + printf("Adding big NULL record\n"); + err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0); + if (err) printf("Failed: %d\n", err);else printf("Succeeded\n"); + timeOut = LONG_TIME; +#if _DNS_SD_LIBDISPATCH + if (timer_source) + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); +#endif + } + break; + } + + if (err != kDNSServiceErr_NoError) + { + fprintf(stderr, "DNSService add/update/remove failed %ld\n", (long int)err); + stopNow = 1; + } +} + +static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode, + const char *name, const char *regtype, const char *domain, void *context) +{ + (void)sdref; // Unused + (void)flags; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + printtimestamp(); + printf("Got a reply for service %s.%s%s: ", name, regtype, domain); + + if (errorCode == kDNSServiceErr_NoError) + { + if (flags & kDNSServiceFlagsAdd) printf("Name now registered and active\n"); + else printf("Name registration removed\n"); + if (operation == 'A' || operation == 'U' || operation == 'N') + { + timeOut = 5; +#if _DNS_SD_LIBDISPATCH + if (timer_source) + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); +#endif + } + } + else if (errorCode == kDNSServiceErr_NameConflict) + { + printf("Name in use, please choose another\n"); + exit(-1); + } + else + printf("Error %d\n", errorCode); + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} + +// Output the wire-format domainname pointed to by rd +static int snprintd(char *p, int max, const unsigned char **rd) +{ + const char *const buf = p; + const char *const end = p + max; + while (**rd) + { + p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); + *rd += 1 + **rd; + } + *rd += 1; // Advance over the final zero byte + return(p-buf); +} + +static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, char *p, unsigned const char *rd, uint16_t rdlen) +{ + int rdb_size = 1000; + switch (rrtype) + { + case kDNSServiceType_DS: + { + unsigned char *ptr; + int i; + rdataDS *rrds = (rdataDS *)rd; + p += snprintf(p, rdb + rdb_size - p, "%d %d %d ", + rrds->alg, swap16(rrds->keyTag), rrds->digestType); + ptr = (unsigned char *)(rd + DS_FIXED_SIZE); + for (i = 0; i < (rdlen - DS_FIXED_SIZE); i++) + p += snprintf(p, rdb + rdb_size - p, "%x", ptr[i]); + break; + } + + case kDNSServiceType_DNSKEY: + { + rdataDNSKey *rrkey = (rdataDNSKey *)rd; + p += snprintf(p, rdb + rdb_size - p, "%d %d %d %u", swap16(rrkey->flags), rrkey->proto, + rrkey->alg, (unsigned int)keytag((unsigned char *)rrkey, rdlen)); + base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + DNSKEY_FIXED_SIZE), rdlen - DNSKEY_FIXED_SIZE); + break; + } + + case kDNSServiceType_NSEC: + { + unsigned char *next = (unsigned char *)rd; + int len, bitmaplen; + int win, wlen, type; + unsigned char *bmap; + char *l = NULL; + + l = p; + p += snprintd(p, rdb + rdb_size - p, &rd); + len = p - l + 1; + + bitmaplen = rdlen - len; + bmap = (unsigned char *)((unsigned char *)next + len); + + while (bitmaplen > 0) + { + int i; + + if (bitmaplen < 3) + { + printf("Case NSEC: malformed nsec, bitmaplen %d short\n", bitmaplen); + break; + } + + win = *bmap++; + wlen = *bmap++; + bitmaplen -= 2; + if (bitmaplen < wlen || wlen < 1 || wlen > 32) + { + printf("Case NSEC: malformed nsec, bitmaplen %d wlen %d\n", bitmaplen, wlen); + break; + } + if (win < 0 || win >= 256) + { + printf("Case NSEC: malformed nsec, bad window win %d\n", win); + break; + } + type = win * 256; + for (i = 0; i < wlen * 8; i++) + { + if (bmap[i>>3] & (128 >> (i&7))) + p += snprintf(p, rdb + rdb_size - p, " %s ", DNSTypeName(type + i)); + } + bmap += wlen; + bitmaplen -= wlen; + } + break; + } + + case kDNSServiceType_RRSIG: + { + rdataRRSig *rrsig = (rdataRRSig *)rd; + unsigned char expTimeBuf[64]; + unsigned char inceptTimeBuf[64]; + unsigned long inceptClock; + unsigned long expClock; + const unsigned char *q = NULL; + char *k = NULL; + int len; + + expClock = (unsigned long)swap32(rrsig->sigExpireTime); + FormatTime(expClock, expTimeBuf, sizeof(expTimeBuf)); + + inceptClock = (unsigned long)swap32(rrsig->sigInceptTime); + FormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf)); + + p += snprintf(p, rdb + rdb_size - p, " %-7s %d %d %d %s %s %7d ", + DNSTypeName(swap16(rrsig->typeCovered)), rrsig->alg, rrsig->labels, swap32(rrsig->origTTL), + expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag)); + + q = (const unsigned char *)&rrsig->signerName; + k = p; + p += snprintd(p, rdb + rdb_size - p, &q); + len = p - k + 1; + + base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + len + RRSIG_FIXED_SIZE), rdlen - (len + RRSIG_FIXED_SIZE)); + break; + } + } + return; +} + +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] = "0.0.0.0", *p = rdb; + int unknowntype = 0; + char dnssec_status[15] = "Unknown"; + char rr_type[RR_TYPE_SIZE]; + char rr_class[3]; + DNSServiceFlags check_flags = flags;//local flags for dnssec status checking + + (void)sdref; // Unused + (void)ifIndex; // Unused + (void)ttl; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (num_printed++ == 0) + { + if (operation == 'D') + printf("Timestamp A/R if %-30s%-6s%-7s%-18s Rdata\n", "Name", "Type", "Class", "DNSSECStatus"); + else + printf("Timestamp A/R Flags if %-30s%-6s%-7s Rdata\n", "Name", "Type", "Class"); + } + printtimestamp(); + + switch (rrclass) + { + case kDNSServiceClass_IN: + strncpy(rr_class, "IN", sizeof(rr_class)); + break; + default: + snprintf(rr_class, sizeof(rr_class), "%d", rrclass); + break; + } + strncpy(rr_type, DNSTypeName(rrtype), sizeof(rr_type)); + + if (!errorCode) //to avoid printing garbage in rdata + { + if (!(check_flags & (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional))) + { + switch (rrtype) + { + case kDNSServiceType_A: + snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); + break; + + case kDNSServiceType_NS: + case kDNSServiceType_CNAME: + case kDNSServiceType_PTR: + case kDNSServiceType_DNAME: + p += snprintd(p, sizeof(rdb), &rd); + break; + + case kDNSServiceType_SOA: + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname + p += snprintf(p, rdb + sizeof(rdb) - p, " "); + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname + p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", + ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4])); + break; + + case kDNSServiceType_AAAA: + snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], + rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); + break; + + case kDNSServiceType_SRV: + p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port + ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); + rd += 6; + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host + break; + + case kDNSServiceType_DS: + case kDNSServiceType_DNSKEY: + case kDNSServiceType_NSEC: + case kDNSServiceType_RRSIG: + ParseDNSSECRecords(rrtype, rdb, p, rd, rdlen); + break; + + default: + snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); + unknowntype = 1; + break; + } + } + else + { + strncpy(rdb, "----", sizeof(rdb)); + //Clear all o/p bits, and then check for dnssec status + check_flags &= ~kDNSServiceOutputFlags; + if (check_flags & kDNSServiceFlagsSecure) + strncpy(dnssec_status, "Secure", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsInsecure) + strncpy(dnssec_status, "Insecure", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsIndeterminate) + strncpy(dnssec_status, "Indeterminate", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsBogus) + strncpy(dnssec_status, "Bogus", sizeof(dnssec_status)); + } + } + + if (operation == 'D') + printf("%s%3d %-30s%-6s%-7s%-18s %s", op, ifIndex, fullname, rr_type, rr_class, dnssec_status, rdb); + else + printf("%s%6X%3d %-30s%-7s%-6s %s", op, flags, ifIndex, fullname, rr_type, rr_class, rdb); + if (unknowntype) + { + while (rd < end) + printf(" %02X", *rd++); + } + if (errorCode) + { + if (errorCode == kDNSServiceErr_NoSuchRecord) + printf(" No Such Record"); + else if (errorCode == kDNSServiceErr_Timeout) + { + printf(" No Such Record\n"); + printf("Query Timed Out\n"); + exit(1); + } + } + printf("\n"); + + if (operation == 'C') + if (flags & kDNSServiceFlagsAdd) + DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata); + + if (!(flags & kDNSServiceFlagsMoreComing)) + fflush(stdout); +} + +#if HAS_NAT_PMP_API +static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, uint32_t publicAddress, uint32_t protocol, uint16_t privatePort, uint16_t publicPort, uint32_t ttl, void *context) +{ + (void)sdref; // Unused + (void)flags; // Unused + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (num_printed++ == 0) printf("Timestamp if %-20s %-15s %-15s %-15s %-6s\n", "External Address", "Protocol", "Internal Port", "External Port", "TTL"); + printtimestamp(); + if (errorCode && errorCode != kDNSServiceErr_DoubleNAT) printf("Error code %d\n", errorCode); + else + { + const unsigned char *digits = (const unsigned char *)&publicAddress; + char addr[256]; + + snprintf(addr, sizeof(addr), "%d.%d.%d.%d", digits[0], digits[1], digits[2], digits[3]); + printf("%-4d %-20s %-15d %-15d %-15d %-6d%s\n", ifIndex, addr, protocol, ntohs(privatePort), ntohs(publicPort), ttl, errorCode == kDNSServiceErr_DoubleNAT ? " Double NAT" : ""); + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} +#endif + +#if HAS_ADDRINFO_API +static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context) +{ + char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + char addr[256] = ""; + char dnssec_status[15] = "Unknown"; + DNSServiceFlags check_flags = flags; + (void) sdref; + (void) context; + + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + if (num_printed++ == 0) + { + if (operation == 'g') + printf("Timestamp A/R if %-25s %-44s %-18s\n", "Hostname", "Address", "DNSSECStatus"); + else + printf("Timestamp A/R Flags if %-38s %-44s %s\n", "Hostname", "Address", "TTL"); + } + printtimestamp(); + + if (address && address->sa_family == AF_INET) + { + const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr; + snprintf(addr, sizeof(addr), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]); + } + else if (address && address->sa_family == AF_INET6) + { + char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE + const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *)address; + const unsigned char *b = (const unsigned char * )&s6->sin6_addr; + if (!if_indextoname(s6->sin6_scope_id, if_name)) + snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id); + snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s", + b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7], + b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name); + } + + //go through this only if you have a dnssec validation status + if (!errorCode && (check_flags & (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional))) + { + strncpy(addr, "----", sizeof(addr)); + //Clear all o/p bits, and then check for dnssec status + check_flags &= ~kDNSServiceOutputFlags; + if (check_flags & kDNSServiceFlagsSecure) + strncpy(dnssec_status, "Secure", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsInsecure) + strncpy(dnssec_status, "Insecure", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsIndeterminate) + strncpy(dnssec_status, "Indeterminate", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsBogus) + strncpy(dnssec_status, "Bogus", sizeof(dnssec_status)); + } + + if (operation == 'g') + printf("%s%3d %-25s %-44s %-18s", op, interfaceIndex, hostname, addr, dnssec_status); + else + printf("%s%6X%3d %-38s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl); + if (errorCode) + { + if (errorCode == kDNSServiceErr_NoSuchRecord) + printf(" No Such Record"); + else + printf(" Error code %d", errorCode); + } + printf("\n"); + + if (!(flags & kDNSServiceFlagsMoreComing)) + fflush(stdout); +} +#endif + +//************************************************************************************************************* +// The main test function + +static void HandleEvents(void) +#if _DNS_SD_LIBDISPATCH +{ + main_queue = dispatch_get_main_queue(); + if (client) DNSServiceSetDispatchQueue(client, main_queue); + if (client_pa) DNSServiceSetDispatchQueue(client_pa, main_queue); + if (operation == 'A' || operation == 'U' || operation == 'N') + { + timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_queue); + if (timer_source) + { + // Start the timer "timeout" seconds into the future and repeat it every "timeout" seconds + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); + dispatch_source_set_event_handler(timer_source, ^{myTimerCallBack();}); + dispatch_resume(timer_source); + } + } + dispatch_main(); +} +#else +{ + int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1; + int dns_sd_fd2 = client_pa ? DNSServiceRefSockFD(client_pa) : -1; + int nfds = dns_sd_fd + 1; + fd_set readfds; + struct timeval tv; + int result; + + if (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1; + + while (!stopNow) + { + // 1. Set up the fd_set as usual here. + // This example client has no file descriptors of its own, + // but a real application would call FD_SET to add them to the set here + FD_ZERO(&readfds); + + // 2. Add the fd for our client(s) to the fd_set + if (client ) FD_SET(dns_sd_fd, &readfds); + if (client_pa) FD_SET(dns_sd_fd2, &readfds); + + // 3. Set up the timeout. + tv.tv_sec = timeOut; + tv.tv_usec = 0; + + result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (result > 0) + { + DNSServiceErrorType err = kDNSServiceErr_NoError; + if (client && FD_ISSET(dns_sd_fd, &readfds)) err = DNSServiceProcessResult(client ); + else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa); + if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } + } + else if (result == 0) + myTimerCallBack(); + else + { + printf("select() returned %d errno %d %s\n", result, errno, strerror(errno)); + if (errno != EINTR) stopNow = 1; + } + } +} +#endif + +static int getfirstoption(int argc, char **argv, const char *optstr, int *pOptInd) +// Return the recognized option in optstr and the option index of the next arg. +#if NOT_HAVE_GETOPT +{ + int i; + for (i=1; i < argc; i++) + { + if (argv[i][0] == '-' && &argv[i][1] && + NULL != strchr(optstr, argv[i][1])) + { + *pOptInd = i + 1; + return argv[i][1]; + } + } + return -1; +} +#else +{ + int o = getopt(argc, (char *const *)argv, optstr); + *pOptInd = optind; + return o; +} +#endif + +static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef rec, const DNSServiceFlags flags, + DNSServiceErrorType errorCode, void *context) +{ + char *name = (char *)context; + + (void)service; // Unused + (void)rec; // Unused + (void)flags; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + + printtimestamp(); + printf("Got a reply for record %s: ", name); + + switch (errorCode) + { + case kDNSServiceErr_NoError: printf("Name now registered and active\n"); break; + case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); exit(-1); + default: printf("Error %d\n", errorCode); break; + } + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + // DNSServiceRemoveRecord(service, rec, 0); to test record removal + +#if 0 // To test updating of individual records registered via DNSServiceRegisterRecord + if (!errorCode) + { + int x = 0x11111111; + printf("Updating\n"); + DNSServiceUpdateRecord(service, rec, 0, sizeof(x), &x, 0); + } +#endif + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); +} + +static void getip(const char *const name, struct sockaddr_storage *result) +{ + struct addrinfo *addrs = NULL; + int err = getaddrinfo(name, NULL, NULL, &addrs); + if (err) fprintf(stderr, "getaddrinfo error %d for %s", err, name); + else memcpy(result, addrs->ai_addr, SA_LEN(addrs->ai_addr)); + if (addrs) freeaddrinfo(addrs); +} + +static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip, DNSServiceFlags flags) +{ + // Call getip() after the call DNSServiceCreateConnection(). + // On the Win32 platform, WinSock must be initialized for getip() to succeed. + // Any DNSService* call will initialize WinSock for us, so we make sure + // DNSServiceCreateConnection() is called before getip() is. + struct sockaddr_storage hostaddr; + getip(ip, &hostaddr); + flags |= kDNSServiceFlagsUnique; + if (hostaddr.ss_family == AF_INET) + return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host, + kDNSServiceType_A, kDNSServiceClass_IN, 4, &((struct sockaddr_in *)&hostaddr)->sin_addr, 240, MyRegisterRecordCallback, (void*)host)); + else if (hostaddr.ss_family == AF_INET6) + return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host, + kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host)); + else return(kDNSServiceErr_BadParam); +} + +#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ + ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ + ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : 0) + +#define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1])) + +static DNSServiceErrorType RegisterService(DNSServiceRef *sdref, + const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags) +{ + uint16_t PortAsNumber = atoi(port); + Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; + unsigned char txt[2048] = ""; + unsigned char *ptr = txt; + int i; + + if (nam[0] == '.' && nam[1] == 0) nam = ""; // We allow '.' on the command line as a synonym for empty string + if (dom[0] == '.' && dom[1] == 0) dom = ""; // We allow '.' on the command line as a synonym for empty string + + printf("Registering Service %s.%s%s%s", nam[0] ? nam : "<<Default>>", typ, dom[0] ? "." : "", dom); + if (host && *host) printf(" host %s", host); + printf(" port %s", port); + + if (argc) + { + for (i = 0; i < argc; i++) + { + const char *p = argv[i]; + *ptr = 0; + while (*p && *ptr < 255 && ptr + 1 + *ptr < txt+sizeof(txt)) + { + if (p[0] != '\\' || p[1] == 0) { ptr[++*ptr] = *p; p+=1; } + else if (p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3])) { ptr[++*ptr] = HexPair(p+2); p+=4; } + else { ptr[++*ptr] = p[1]; p+=2; } + } + ptr += 1 + *ptr; + } + printf(" TXT"); + ShowTXTRecord(ptr-txt, txt); + } + printf("\n"); + + //flags |= kDNSServiceFlagsAllowRemoteQuery; + //flags |= kDNSServiceFlagsNoAutoRename; + + return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL)); +} + +#define TypeBufferSize 80 +static char *gettype(char *buffer, char *typ) +{ + if (!typ || !*typ || (typ[0] == '.' && typ[1] == 0)) typ = "_http._tcp"; + if (!strchr(typ, '.')) { snprintf(buffer, TypeBufferSize, "%s._tcp", typ); typ = buffer; } + return(typ); +} + +int main(int argc, char **argv) +{ + DNSServiceErrorType err; + char buffer[TypeBufferSize], *typ, *dom; + int opi; + DNSServiceFlags flags = 0; + int optional = 0; + + // Extract the program name from argv[0], which by convention contains the path to this executable. + // Note that this is just a voluntary convention, not enforced by the kernel -- + // the process calling exec() can pass bogus data in argv[0] if it chooses to. + const char *a0 = strrchr(argv[0], kFilePathSep) + 1; + if (a0 == (const char *)1) a0 = argv[0]; + +#if defined(_WIN32) + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); +#endif + +#if TEST_NEW_CLIENTSTUB + printf("Using embedded copy of dnssd_clientstub instead of system library\n"); + if (sizeof(argv) == 8) printf("Running in 64-bit mode\n"); +#endif + + // Test code for TXTRecord functions + //TXTRecordRef txtRecord; + //TXTRecordCreate(&txtRecord, 0, NULL); + //TXTRecordSetValue(&txtRecord, "aaa", 1, "b"); + //printf("%d\n", TXTRecordContainsKey(TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), "Aaa")); + + if (argc > 1 && !strcmp(argv[1], "-lo")) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexLocalOnly; + printf("Using LocalOnly\n"); + } + + if (argc > 1 && (!strcmp(argv[1], "-p2p") || !strcmp(argv[1], "-P2P"))) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexP2P; + } + + if (argc > 1 && !strcasecmp(argv[1], "-includep2p")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsIncludeP2P; + printf("Setting kDNSServiceFlagsIncludeP2P\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-includeAWDL")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsIncludeAWDL; + printf("Setting kDNSServiceFlagsIncludeAWDL\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-tc")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsBackgroundTrafficClass; + printf("Setting kDNSServiceFlagsBackgroundTrafficClass\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-t1")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsThresholdOne; + printf("Setting kDNSServiceFlagsThresholdOne\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-tFinder")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsThresholdFinder; + printf("Setting kDNSServiceFlagsThresholdFinder\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-wo")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsWakeOnlyService; + printf("Setting kDNSServiceFlagsWakeOnlyService\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-unicastResponse")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsUnicastResponse; + printf("Setting kDNSServiceFlagsUnicastResponse\n"); + } + if (argc > 1 && !strcasecmp(argv[1], "-timeout")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsTimeout; + printf("Setting kDNSServiceFlagsTimeout\n"); + } + if (argc > 1 && !strcasecmp(argv[1], "-optional")) + { + argc--; + argv++; + optional = 1; + printf("Setting DNSSEC optional flag\n"); + } + + if (argc > 2 && !strcmp(argv[1], "-i")) + { + opinterface = if_nametoindex(argv[2]); + if (!opinterface) opinterface = atoi(argv[2]); + if (!opinterface) { fprintf(stderr, "Unknown interface %s\n", argv[2]); goto Fail; } + argc -= 2; + argv += 2; + } + + if (argc < 2) goto Fail; // Minimum command line is the command name and one argument + operation = getfirstoption(argc, argv, "EFBZLlRPQqCAUNTMISVHhD" + #if HAS_NAT_PMP_API + "X" + #endif + #if HAS_ADDRINFO_API + "Gg" + #endif + , &opi); + if (operation == -1) goto Fail; + + if (opinterface) printf("Using interface %d\n", opinterface); + + switch (operation) + { + case 'E': printf("Looking for recommended registration domains:\n"); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, opinterface, enum_reply, NULL); + break; + + case 'F': printf("Looking for recommended browsing domains:\n"); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, opinterface, enum_reply, NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "bonjour.nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "ibm.com.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL); + break; + + case 'B': typ = (argc < opi+1) ? "" : argv[opi+0]; + dom = (argc < opi+2) ? "" : argv[opi+1]; // Missing domain argument is the same as empty string i.e. use system default(s) + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string + printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); + err = DNSServiceBrowse(&client, flags, opinterface, typ, dom, browse_reply, NULL); + break; + + case 'Z': typ = (argc < opi+1) ? "" : argv[opi+0]; + dom = (argc < opi+2) ? "" : argv[opi+1]; // Missing domain argument is the same as empty string i.e. use system default(s) + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string + printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); + err = DNSServiceCreateConnection(&client); + sc1 = client; + err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL); + break; + + case 'l': + case 'L': { + if (argc < opi+2) goto Fail; + typ = (argc < opi+2) ? "" : argv[opi+1]; + dom = (argc < opi+3) ? "local" : argv[opi+2]; + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" + printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom); + if (operation == 'l') flags |= kDNSServiceFlagsWakeOnResolve; + err = DNSServiceResolve(&client, flags, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL); + break; + } + + case 'R': if (argc < opi+4) goto Fail; + typ = (argc < opi+2) ? "" : argv[opi+1]; + dom = (argc < opi+3) ? "" : argv[opi+2]; + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string + err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags); + break; + + + case 'P': if (argc < opi+6) goto Fail; + err = DNSServiceCreateConnection(&client_pa); + if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } + err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5], flags); + if (err) break; + err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags); + break; + + case 'D': + case 'q': + case 'Q': + case 'C': { + uint16_t rrtype, rrclass; + flags |= kDNSServiceFlagsReturnIntermediates; + if (operation == 'q') + flags |= kDNSServiceFlagsSuppressUnusable; + if (argc < opi+1) + goto Fail; + rrtype = (argc <= opi+1) ? kDNSServiceType_A : GetRRType(argv[opi+1]); + rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : GetRRClass(argv[opi+2]); + if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) + flags |= kDNSServiceFlagsLongLivedQuery; + if (operation == 'D') + { + flags |= kDNSServiceFlagsSuppressUnusable; + if (optional) + flags |= kDNSServiceFlagsValidateOptional; + else + flags |= kDNSServiceFlagsValidate; + } + err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL); + break; + } + + case 'A': + case 'U': + case 'N': { + Opaque16 registerPort = { { 0x12, 0x34 } }; + static const char TXT[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; + printf("Registering Service Test._testupdate._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testupdate._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL); + break; + } + + case 'T': { + Opaque16 registerPort = { { 0x23, 0x45 } }; + char TXT[1024]; + unsigned int i; + for (i=0; i<sizeof(TXT); i++) + if ((i & 0x1F) == 0) TXT[i] = 0x1F;else TXT[i] = 'A' + (i >> 5); + printf("Registering Service Test._testlargetxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testlargetxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT), TXT, reg_reply, NULL); + break; + } + + case 'M': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT1[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; + static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String" "\xC" "Sixth String"; + printf("Registering Service Test._testdualtxt._tcp.local.\n"); + err = DNSServiceRegister(&client, flags, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL); + if (!err) err = DNSServiceAddRecord(client, &record, flags, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0); + break; + } + + case 'I': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT[] = "\x09" "Test Data"; + printf("Registering Service Test._testtxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); + if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0); + break; + } + +#if HAS_NAT_PMP_API + case 'X': { + if (argc == opi) // If no arguments, just fetch IP address + err = DNSServiceNATPortMappingCreate(&client, 0, 0, 0, 0, 0, 0, port_mapping_create_reply, NULL); + else if (argc >= opi+2 && atoi(argv[opi+0]) == 0) + { + DNSServiceProtocol prot = GetProtocol(argv[opi+0]); // Must specify TCP or UDP + uint16_t IntPortAsNumber = atoi(argv[opi+1]); // Must specify internal port + uint16_t ExtPortAsNumber = (argc < opi+3) ? 0 : atoi(argv[opi+2]); // Optional desired external port + uint32_t ttl = (argc < opi+4) ? 0 : atoi(argv[opi+3]); // Optional desired lease lifetime + Opaque16 intp = { { IntPortAsNumber >> 8, IntPortAsNumber & 0xFF } }; + Opaque16 extp = { { ExtPortAsNumber >> 8, ExtPortAsNumber & 0xFF } }; + err = DNSServiceNATPortMappingCreate(&client, 0, 0, prot, intp.NotAnInteger, extp.NotAnInteger, ttl, port_mapping_create_reply, NULL); + } + else goto Fail; + break; + } +#endif + +#if HAS_ADDRINFO_API + case 'g': + case 'G': { + flags |= kDNSServiceFlagsReturnIntermediates; + if (operation == 'g') + { + flags |= kDNSServiceFlagsSuppressUnusable; + if (optional) + flags |= kDNSServiceFlagsValidateOptional; + else + flags |= kDNSServiceFlagsValidate; + } + if (argc != opi+2) + goto Fail; + else + err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL); + break; + } +#endif + + case 'S': { + Opaque16 registerPort = { { 0x23, 0x45 } }; // 9029 decimal + unsigned char txtrec[16] = "\xF" "/path=test.html"; + DNSRecordRef rec; + unsigned char nulrec[4] = "1234"; + + err = DNSServiceCreateConnection(&client); + if (err) { fprintf(stderr, "DNSServiceCreateConnection failed %ld\n", (long int)err); return (-1); } + + sc1 = client; + err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, "_http._tcp", "", browse_reply, NULL); + if (err) { fprintf(stderr, "DNSServiceBrowse _http._tcp failed %ld\n", (long int)err); return (-1); } + + sc2 = client; + err = DNSServiceBrowse(&sc2, kDNSServiceFlagsShareConnection, opinterface, "_ftp._tcp", "", browse_reply, NULL); + if (err) { fprintf(stderr, "DNSServiceBrowse _ftp._tcp failed %ld\n", (long int)err); return (-1); } + + sc3 = client; + err = DNSServiceRegister(&sc3, kDNSServiceFlagsShareConnection, opinterface, "kDNSServiceFlagsShareConnection", + "_http._tcp", "local", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); + if (err) { fprintf(stderr, "SharedConnection DNSServiceRegister failed %ld\n", (long int)err); return (-1); } + + err = DNSServiceUpdateRecord(sc3, NULL, 0, sizeof(txtrec), txtrec, 0); + if (err) { fprintf(stderr, "SharedConnection DNSServiceUpdateRecord failed %ld\n", (long int)err); return (-1); } + + err = DNSServiceAddRecord(sc3, &rec, 0, kDNSServiceType_NULL, sizeof(nulrec), nulrec, 0); + if (err) { fprintf(stderr, "SharedConnection DNSServiceAddRecord failed %ld\n", (long int)err); return (-1); } + + err = DNSServiceRemoveRecord(sc3, rec, 0); + if (err) { fprintf(stderr, "SharedConnection DNSServiceRemoveRecord failed %ld\n", (long int)err); return (-1); } + + break; + } + + case 'V': { + uint32_t v; + uint32_t size = sizeof(v); + err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size); + if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err); + else printf("Currently running daemon (system service) is version %d.%d.%d\n", v / 10000, v / 100 % 100, v % 100); + exit(0); + } + + case 'H': goto Fail; + + default: goto Fail; + } + + if (!client || err != kDNSServiceErr_NoError) + { + fprintf(stderr, "DNSService call failed %ld%s\n", (long int)err, + (err == kDNSServiceErr_ServiceNotRunning) ? " (Service Not Running)" : ""); + return (-1); + } + printtimestamp(); + printf("...STARTING...\n"); + HandleEvents(); + + // Be sure to deallocate the DNSServiceRef when you're finished + if (client ) DNSServiceRefDeallocate(client ); + if (client_pa) DNSServiceRefDeallocate(client_pa); + return 0; + +Fail: + if (operation == 'H') print_usage(a0,1); + else print_usage(a0,0); + return 0; + +} + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +// NOT static -- otherwise the compiler may optimize it out +// The "@(#) " pattern is a special prefix the "what" command looks for +const char VersionString_SCCS[] = "@(#) dns-sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; + +#if _BUILDING_XCODE_PROJECT_ +// If the process crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = VersionString_SCCS + 5; +asm (".desc ___crashreporter_info__, 0x10"); +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/CryptoAlg.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/CryptoAlg.c new file mode 100644 index 0000000000..38533fc886 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/CryptoAlg.c @@ -0,0 +1,280 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 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. + */ + +// *************************************************************************** +// CryptoAlg.c: +// Interface to DNSSEC cryptographic algorithms. The crypto support itself is +// provided by the platform and the functions in this file just provide an +// interface to access them in a more generic way. +// *************************************************************************** + +#include "mDNSEmbeddedAPI.h" +#include "CryptoAlg.h" + +AlgFuncs *DigestAlgFuncs[DIGEST_TYPE_MAX]; +AlgFuncs *CryptoAlgFuncs[CRYPTO_ALG_MAX]; +AlgFuncs *EncAlgFuncs[ENC_ALG_MAX]; + +mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func) +{ + if (digestType >= DIGEST_TYPE_MAX) + { + LogMsg("DigestAlgInit: digestType %d exceeds bounds", digestType); + return mStatus_BadParamErr; + } + // As digestTypes may not be consecutive, check for specific digest types + // that we support + if (digestType != SHA1_DIGEST_TYPE && + digestType != SHA256_DIGEST_TYPE) + { + LogMsg("DigestAlgInit: digestType %d not supported", digestType); + return mStatus_BadParamErr; + } + DigestAlgFuncs[digestType] = func; + return mStatus_NoError; +} + +mDNSexport mStatus CryptoAlgInit(mDNSu8 alg, AlgFuncs *func) +{ + if (alg >= CRYPTO_ALG_MAX) + { + LogMsg("CryptoAlgInit: alg %d exceeds bounds", alg); + return mStatus_BadParamErr; + } + // As algs may not be consecutive, check for specific algorithms + // that we support + if (alg != CRYPTO_RSA_SHA1 && alg != CRYPTO_RSA_SHA256 && alg != CRYPTO_RSA_SHA512 && + alg != CRYPTO_DSA_NSEC3_SHA1 && alg != CRYPTO_RSA_NSEC3_SHA1) + { + LogMsg("CryptoAlgInit: alg %d not supported", alg); + return mStatus_BadParamErr; + } + + CryptoAlgFuncs[alg] = func; + return mStatus_NoError; +} + +mDNSexport mStatus EncAlgInit(mDNSu8 alg, AlgFuncs *func) +{ + if (alg >= ENC_ALG_MAX) + { + LogMsg("EncAlgInit: alg %d exceeds bounds", alg); + return mStatus_BadParamErr; + } + + // As algs may not be consecutive, check for specific algorithms + // that we support + if (alg != ENC_BASE32 && alg != ENC_BASE64) + { + LogMsg("EncAlgInit: alg %d not supported", alg); + return mStatus_BadParamErr; + } + + EncAlgFuncs[alg] = func; + return mStatus_NoError; +} + +mDNSexport AlgContext *AlgCreate(AlgType type, mDNSu8 alg) +{ + AlgFuncs *func = mDNSNULL; + AlgContext *ctx; + + if (type == CRYPTO_ALG) + { + if (alg >= CRYPTO_ALG_MAX) return mDNSNULL; + func = CryptoAlgFuncs[alg]; + } + else if (type == DIGEST_ALG) + { + if (alg >= DIGEST_TYPE_MAX) return mDNSNULL; + func = DigestAlgFuncs[alg]; + } + else if (type == ENC_ALG) + { + if (alg >= ENC_ALG_MAX) return mDNSNULL; + func = EncAlgFuncs[alg]; + } + + if (!func) + { + // If there is no support from the platform, this case can happen. + LogInfo("AlgCreate: func is NULL"); + return mDNSNULL; + } + + if (func->Create) + { + mStatus err; + ctx = mDNSPlatformMemAllocate(sizeof(AlgContext)); + if (!ctx) return mDNSNULL; + // Create expects ctx->alg to be initialized + ctx->alg = alg; + err = func->Create(ctx); + if (err == mStatus_NoError) + { + ctx->type = type; + return ctx; + } + mDNSPlatformMemFree(ctx); + } + return mDNSNULL; +} + +mDNSexport mStatus AlgDestroy(AlgContext *ctx) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + if (!func) + { + LogMsg("AlgDestroy: ERROR!! func is NULL"); + mDNSPlatformMemFree(ctx); + return mStatus_BadParamErr; + } + + if (func->Destroy) + func->Destroy(ctx); + + mDNSPlatformMemFree(ctx); + return mStatus_NoError; +} + +mDNSexport mDNSu32 AlgLength(AlgContext *ctx) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgLength: ERROR!! func is NULL"); + return 0; + } + + if (func->Length) + return (func->Length(ctx)); + else + return 0; +} + +mDNSexport mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgAdd: ERROR!! func is NULL"); + return mStatus_BadParamErr; + } + + if (func->Add) + return (func->Add(ctx, data, len)); + else + return mStatus_BadParamErr; +} + +mDNSexport mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgVerify: ERROR!! func is NULL"); + return mStatus_BadParamErr; + } + + if (func->Verify) + return (func->Verify(ctx, key, keylen, signature, siglen)); + else + return mStatus_BadParamErr; +} + +mDNSexport mDNSu8* AlgEncode(AlgContext *ctx) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgEncode: ERROR!! func is NULL"); + return mDNSNULL; + } + + if (func->Encode) + return (func->Encode(ctx)); + else + return mDNSNULL; +} + +mDNSexport mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgEncode: ERROR!! func is NULL"); + return mDNSNULL; + } + + if (func->Final) + return (func->Final(ctx, data, len)); + else + return mStatus_BadParamErr; +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/CryptoAlg.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/CryptoAlg.h new file mode 100644 index 0000000000..6cb3dc9d24 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/CryptoAlg.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __CRYPTO_ALG_H +#define __CRYPTO_ALG_H + +typedef enum +{ + CRYPTO_ALG, + DIGEST_ALG, + ENC_ALG, +} AlgType; + +typedef struct +{ + void *context; + AlgType type; + mDNSu8 alg; +} AlgContext; + +typedef struct +{ + mStatus (*Create)(AlgContext *ctx); + mStatus (*Destroy)(AlgContext *ctx); + mDNSu32 (*Length)(AlgContext *ctx); + mStatus (*Add)(AlgContext *ctx, const void *data, mDNSu32 len); + // Verify the ctx using the key and compare it against signature/siglen + mStatus (*Verify)(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen); + // Encode the data and return the encoded data + mDNSu8* (*Encode)(AlgContext *ctx); + // Return the finalized data in data whose length is len (used by hash algorithms) + mStatus (*Final)(AlgContext *ctx, void *data, mDNSu32 len); +} AlgFuncs; + +mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func); +mDNSexport mStatus CryptoAlgInit(mDNSu8 algType, AlgFuncs *func); +mDNSexport mStatus EncAlgInit(mDNSu8 algType, AlgFuncs *func); + + +extern AlgContext *AlgCreate(AlgType type, mDNSu8 alg); +extern mStatus AlgDestroy(AlgContext *ctx); +extern mDNSu32 AlgLength(AlgContext *ctx); +extern mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len); +extern mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen); +extern mDNSu8* AlgEncode(AlgContext *ctx); +extern mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len); + +#endif // __CRYPTO_ALG_H diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c index 173a8f8325..a0dbfcd728 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c @@ -1,405 +1,114 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 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" +#include "CryptoAlg.h" +#include "anonymous.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) +// 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 +#pragma mark - Program Constants #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); - } - } +mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; +mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1; +mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2; +mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3; +mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4; +mDNSexport const mDNSInterfaceID uDNSInterfaceMark = (mDNSInterfaceID)-5; + +// Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of +// Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP +// port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders. +// LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355. +// Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability +// with Microsoft's LLMNR client code. + +#define DiscardPortAsNumber 9 +#define SSHPortAsNumber 22 +#define UnicastDNSPortAsNumber 53 +#define SSDPPortAsNumber 1900 +#define IPSECPortAsNumber 4500 +#define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback +#define NATPMPAnnouncementPortAsNumber 5350 +#define NATPMPPortAsNumber 5351 +#define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc. +#define MulticastDNSPortAsNumber 5353 +#define LoopbackIPCPortAsNumber 5354 +//#define MulticastDNSPortAsNumber 5355 // LLMNR +#define PrivateDNSPortAsNumber 5533 + +mDNSexport const mDNSIPPort DiscardPort = { { DiscardPortAsNumber >> 8, DiscardPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort SSHPort = { { SSHPortAsNumber >> 8, SSHPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort SSDPPort = { { SSDPPortAsNumber >> 8, SSDPPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort IPSECPort = { { IPSECPortAsNumber >> 8, IPSECPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort NSIPCPort = { { NSIPCPortAsNumber >> 8, NSIPCPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } }; + +mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } }; + +mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; +mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; +mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; +mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } }; +mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; +mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; +mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } }; +mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; + +mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; +mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP & PCP Annoucements +mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } }; +mDNSexport const mDNSv6Addr NDP_prefix = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104 +mDNSexport const mDNSEthAddr AllHosts_v6_Eth = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } }; +mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; +//mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR +mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; +//mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR + +mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; +mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } }; +mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; +mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } }; +mDNSexport const mDNSOpaque16 DNSSecQFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, kDNSFlag1_CD } }; +mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; +mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; +mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; + +mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } }; // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -408,141 +117,501 @@ mDNSexport void mDNS_FreeDNameList(DNameListElem *list) #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 mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr) +{ + return ((addr->b[0] == 10) || // 10/8 prefix + (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12 + (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16 +} + +mDNSexport void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out) +{ + out->l[0] = 0; + out->l[1] = 0; + out->w[4] = 0; + out->w[5] = 0xffff; + out->b[12] = in->b[0]; + out->b[13] = in->b[1]; + out->b[14] = in->b[2]; + out->b[15] = in->b[3]; +} + +mDNSexport mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr* out) +{ + if (in->l[0] != 0 || in->l[1] != 0 || in->w[4] != 0 || in->w[5] != 0xffff) + return mDNSfalse; + + out->NotAnInteger = in->l[3]; + return mDNStrue; +} + +mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf) +{ + while (intf && !intf->InterfaceActive) intf = intf->next; + return(intf); +} mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf) - { - const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); - if (next) return(next->InterfaceID); else return(mDNSNULL); - } +{ + 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); - } +{ + mDNSu32 slot, used = 0; + CacheGroup *cg; + const CacheRecord *rr; + FORALL_CACHERECORDS(slot, cg, rr) + { + if (rr->resrec.InterfaceID == id) + used++; + } + return(used); +} mDNSexport char *DNSTypeName(mDNSu16 rrtype) - { - switch (rrtype) - { - case kDNSType_A: return("Addr"); - case kDNSType_NS: return("NS"); - case kDNSType_CNAME:return("CNAME"); - case kDNSType_SOA: return("SOA"); - case kDNSType_NULL: return("NULL"); - case kDNSType_PTR: return("PTR"); - case kDNSType_HINFO:return("HINFO"); - case kDNSType_TXT: return("TXT"); - case kDNSType_AAAA: return("AAAA"); - case kDNSType_SRV: return("SRV"); - case kDNSQType_ANY: return("ANY"); - default: { - static char buffer[16]; - mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype); - return(buffer); - } - } - } +{ + switch (rrtype) + { + case kDNSType_A: return("Addr"); + case kDNSType_NS: return("NS"); + case kDNSType_CNAME: return("CNAME"); + case kDNSType_SOA: return("SOA"); + case kDNSType_NULL: return("NULL"); + case kDNSType_PTR: return("PTR"); + case kDNSType_HINFO: return("HINFO"); + case kDNSType_TXT: return("TXT"); + case kDNSType_AAAA: return("AAAA"); + case kDNSType_SRV: return("SRV"); + case kDNSType_OPT: return("OPT"); + case kDNSType_NSEC: return("NSEC"); + case kDNSType_NSEC3: return("NSEC3"); + case kDNSType_NSEC3PARAM: return("NSEC3PARAM"); + case kDNSType_TSIG: return("TSIG"); + case kDNSType_RRSIG: return("RRSIG"); + case kDNSType_DNSKEY: return("DNSKEY"); + case kDNSType_DS: return("DS"); + case kDNSQType_ANY: return("ANY"); + default: { + static char buffer[16]; + mDNS_snprintf(buffer, sizeof(buffer), "TYPE%d", rrtype); + return(buffer); + } + } +} + +mDNSlocal char *DNSSECAlgName(mDNSu8 alg) +{ + switch (alg) + { + case CRYPTO_RSA_SHA1: return "RSA_SHA1"; + case CRYPTO_DSA_NSEC3_SHA1: return "DSA_NSEC3_SHA1"; + case CRYPTO_RSA_NSEC3_SHA1: return "RSA_NSEC3_SHA1"; + case CRYPTO_RSA_SHA256: return "RSA_SHA256"; + case CRYPTO_RSA_SHA512: return "RSA_SHA512"; + default: { + static char algbuffer[16]; + mDNS_snprintf(algbuffer, sizeof(algbuffer), "ALG%d", alg); + return(algbuffer); + } + } +} + +mDNSlocal char *DNSSECDigestName(mDNSu8 digest) +{ + switch (digest) + { + case SHA1_DIGEST_TYPE: return "SHA1"; + case SHA256_DIGEST_TYPE: return "SHA256"; + default: + { + static char digbuffer[16]; + mDNS_snprintf(digbuffer, sizeof(digbuffer), "DIG%d", digest); + return(digbuffer); + } + } +} + +mDNSexport mDNSu32 swap32(mDNSu32 x) +{ + mDNSu8 *ptr = (mDNSu8 *)&x; + return (mDNSu32)((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); +} + +mDNSexport mDNSu16 swap16(mDNSu16 x) +{ + mDNSu8 *ptr = (mDNSu8 *)&x; + return (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); +} + +// RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted +// explicitly on the wire. +// +// Note: This just helps narrow down the list of keys to look at. It is possible +// for two DNS keys to have the same ID i.e., key ID is not a unqiue tag. We ignore +// MD5 keys. +// +// 1st argument - the RDATA part of the DNSKEY RR +// 2nd argument - the RDLENGTH +// +mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize) +{ + unsigned long ac; + unsigned int i; + + for (ac = 0, i = 0; i < keysize; ++i) + ac += (i & 1) ? key[i] : key[i] << 8; + ac += (ac >> 16) & 0xFFFF; + return ac & 0xFFFF; +} + +mDNSexport int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg) +{ + AlgContext *ctx; + mDNSu8 *outputBuffer; + int length; + + ctx = AlgCreate(ENC_ALG, encAlg); + if (!ctx) + { + LogMsg("baseEncode: AlgCreate failed\n"); + return 0; + } + AlgAdd(ctx, data, len); + outputBuffer = AlgEncode(ctx); + length = 0; + if (outputBuffer) + { + // Note: don't include any spaces in the format string below. This + // is also used by NSEC3 code for proving non-existence where it + // needs the base32 encoding without any spaces etc. + length = mDNS_snprintf(buffer, blen, "%s", outputBuffer); + } + AlgDestroy(ctx); + return length; +} + +mDNSlocal void PrintTypeBitmap(const mDNSu8 *bmap, int bitmaplen, char *const buffer, mDNSu32 length) +{ + int win, wlen, type; + + while (bitmaplen > 0) + { + int i; + + if (bitmaplen < 3) + { + LogMsg("PrintTypeBitmap: malformed bitmap, bitmaplen %d short", bitmaplen); + break; + } + + win = *bmap++; + wlen = *bmap++; + bitmaplen -= 2; + if (bitmaplen < wlen || wlen < 1 || wlen > 32) + { + LogInfo("PrintTypeBitmap: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen); + break; + } + if (win < 0 || win >= 256) + { + LogInfo("PrintTypeBitmap: malformed nsec, bad window win %d", win); + break; + } + type = win * 256; + for (i = 0; i < wlen * 8; i++) + { + if (bmap[i>>3] & (128 >> (i&7))) + length += mDNS_snprintf(buffer+length, (MaxMsg - 1) - length, "%s ", DNSTypeName(type + i)); + } + bmap += wlen; + bitmaplen -= wlen; + } +} + +// Parse the fields beyond the base header. NSEC3 should have been validated. +mDNSexport void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap) +{ + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data; + mDNSu8 *p = (mDNSu8 *)&nsec3->salt; + int hlen; + + if (salt) + { + if (nsec3->saltLength) + *salt = p; + else + *salt = mDNSNULL; + } + p += nsec3->saltLength; + // p is pointing at hashLength + hlen = (int)*p; + if (hashLength) + *hashLength = hlen; + p++; + if (nxtName) + *nxtName = p; + p += hlen; + if (bitmaplen) + *bitmaplen = rr->rdlength - (int)(p - rdb->data); + if (bitmap) + *bitmap = p; +} // 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 char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer) +{ + const RDataBody2 *const rd = (RDataBody2 *)rd1; + #define RemSpc (MaxMsg-1-length) + char *ptr = buffer; + mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype)); + if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer); + if (!rr->rdlength && rr->rrtype != kDNSType_OPT) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); } + + switch (rr->rrtype) + { + case kDNSType_A: mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4); break; + + case kDNSType_NS: // Same as PTR + case kDNSType_CNAME: // Same as PTR + case kDNSType_PTR: mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c); break; + + case kDNSType_SOA: mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d", + rd->soa.mname.c, rd->soa.rname.c, + rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min); + break; + + case kDNSType_HINFO: // Display this the same as TXT (show all constituent strings) + case kDNSType_TXT: { + const mDNSu8 *t = rd->txt.c; + while (t < rd->txt.c + rr->rdlength) + { + length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t); + t += 1 + t[0]; + } + } break; + + case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break; + case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s", + rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break; + + case kDNSType_OPT: { + const rdataOPT *opt; + const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength]; + length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass); + for (opt = &rd->opt[0]; opt < end; opt++) + { + switch(opt->opt) + { + case kDNSOpt_LLQ: + length += mDNS_snprintf(buffer+length, RemSpc, " LLQ"); + length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.llq.vers); + length += mDNS_snprintf(buffer+length, RemSpc, " Op %d", opt->u.llq.llqOp); + length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err); + length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]); + length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.llq.llqlease); + break; + case kDNSOpt_Lease: + length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.updatelease); + break; + case kDNSOpt_Owner: + length += mDNS_snprintf(buffer+length, RemSpc, " Owner"); + length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.owner.vers); + length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq); // Display as unsigned + length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a", opt->u.owner.HMAC.b); + if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) + { + length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b); + if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) + length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b); + } + break; + case kDNSOpt_Trace: + length += mDNS_snprintf(buffer+length, RemSpc, " Trace"); + length += mDNS_snprintf(buffer+length, RemSpc, " Platform %d", opt->u.tracer.platf); + length += mDNS_snprintf(buffer+length, RemSpc, " mDNSVers %d", opt->u.tracer.mDNSv); + break; + default: + length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt); + break; + } + } + } + break; + + case kDNSType_NSEC: { + domainname *next = (domainname *)rd->data; + int len, bitmaplen; + mDNSu8 *bmap; + len = DomainNameLength(next); + bitmaplen = rr->rdlength - len; + bmap = (mDNSu8 *)((mDNSu8 *)next + len); + + if (UNICAST_NSEC(rr)) + length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c); + PrintTypeBitmap(bmap, bitmaplen, buffer, length); + + } + break; + case kDNSType_NSEC3: { + rdataNSEC3 *nsec3 = (rdataNSEC3 *)rd->data; + const mDNSu8 *p = (mDNSu8 *)&nsec3->salt; + int hashLength, bitmaplen, i; + + length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %d %d ", + DNSSECDigestName(nsec3->alg), nsec3->flags, swap16(nsec3->iterations)); + + if (!nsec3->saltLength) + { + length += mDNS_snprintf(buffer+length, RemSpc, "-"); + } + else + { + for (i = 0; i < nsec3->saltLength; i++) + { + length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]); + } + } + + // put a space at the end + length += mDNS_snprintf(buffer+length, RemSpc, " "); + + p += nsec3->saltLength; + // p is pointing at hashLength + hashLength = (int)*p++; + + length += baseEncode(buffer + length, RemSpc, p, hashLength, ENC_BASE32); + + // put a space at the end + length += mDNS_snprintf(buffer+length, RemSpc, " "); + + p += hashLength; + bitmaplen = rr->rdlength - (int)(p - rd->data); + PrintTypeBitmap(p, bitmaplen, buffer, length); + } + break; + case kDNSType_RRSIG: { + rdataRRSig *rrsig = (rdataRRSig *)rd->data; + mDNSu8 expTimeBuf[64]; + mDNSu8 inceptTimeBuf[64]; + unsigned long inceptClock; + unsigned long expClock; + int len; + + expClock = (unsigned long)swap32(rrsig->sigExpireTime); + mDNSPlatformFormatTime(expClock, expTimeBuf, sizeof(expTimeBuf)); + + inceptClock = (unsigned long)swap32(rrsig->sigInceptTime); + mDNSPlatformFormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf)); + + length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %s %d %d %s %s %d %##s ", + DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL), + expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), ((domainname *)(&rrsig->signerName))->c); + + len = DomainNameLength((domainname *)&rrsig->signerName); + length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE), + rr->rdlength - (len + RRSIG_FIXED_SIZE), ENC_BASE64); + } + break; + case kDNSType_DNSKEY: { + rdataDNSKey *rrkey = (rdataDNSKey *)rd->data; + length += mDNS_snprintf(buffer+length, RemSpc, "\t%d %d %s %u ", swap16(rrkey->flags), rrkey->proto, + DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength)); + length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE), + rr->rdlength - DNSKEY_FIXED_SIZE, ENC_BASE64); + } + break; + case kDNSType_DS: { + mDNSu8 *p; + int i; + rdataDS *rrds = (rdataDS *)rd->data; + + length += mDNS_snprintf(buffer+length, RemSpc, "\t%s\t%d\t%s ", DNSSECAlgName(rrds->alg), swap16(rrds->keyTag), + DNSSECDigestName(rrds->digestType)); + + p = (mDNSu8 *)(rd->data + DS_FIXED_SIZE); + for (i = 0; i < (rr->rdlength - DS_FIXED_SIZE); i++) + { + length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]); + } + } + break; + + default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data); + // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not + for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.'; + break; + } + return(buffer); +} + +// See comments in mDNSEmbeddedAPI.h +#if _PLATFORM_HAS_STRONG_PRNG_ +#define mDNSRandomNumber mDNSPlatformRandomNumber +#else +mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed) +{ + return seed * 21 + 1; +} + +mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration) +{ + return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed; +} + +mDNSlocal mDNSu32 mDNSRandomNumber() +{ + static mDNSBool seeded = mDNSfalse; + static mDNSu32 seed = 0; + if (!seeded) + { + seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100); + seeded = mDNStrue; + } + return (seed = mDNSRandomFromSeed(seed)); +} +#endif // ! _PLATFORM_HAS_STRONG_PRNG_ + +mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive +{ + mDNSu32 ret = 0; + mDNSu32 mask = 1; + + while (mask < max) mask = (mask << 1) | 1; + + do ret = mDNSRandomNumber() & mask; + while (ret > max); + + return ret; +} mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2) - { - if (ip1->type == ip2->type) - { - switch (ip1->type) - { - case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal - case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4)); - case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6)); - } - } - return(mDNSfalse); - } +{ + 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); - } - } +{ + switch(ip->type) + { + case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4)); + case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6)); + default: return(mDNSfalse); + } +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -551,90 +620,107 @@ mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) #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); - } +{ + 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); - } +{ + const mDNSu8 * a = d1->c; + const mDNSu8 * b = d2->c; + const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid + + while (*a || *b) + { + if (a + 1 + *a >= max) + { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); } + if (!SameDomainLabel(a, b)) return(mDNSfalse); + a += 1 + *a; + b += 1 + *b; + } + + return(mDNStrue); +} + +mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2) +{ + mDNSu16 l1 = DomainNameLength(d1); + mDNSu16 l2 = DomainNameLength(d2); + return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1)); +} mDNSexport mDNSBool IsLocalDomain(const domainname *d) - { - // Domains that are defined to be resolved via link-local multicast are: - // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa. - static const domainname *nL = (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); - } +{ + // Domains that are defined to be resolved via link-local multicast are: + // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa. + static const domainname *nL = (const domainname*)"\x5" "local"; + static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa"; + static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + + const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc. + d1 = d2 = d3 = d4 = d5 = mDNSNULL; + while (d->c[0]) + { + d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d; + d = (const domainname*)(d->c + 1 + d->c[0]); + } + + if (d1 && SameDomainName(d1, nL)) return(mDNStrue); + if (d4 && SameDomainName(d4, nR)) return(mDNStrue); + if (d5 && SameDomainName(d5, n8)) return(mDNStrue); + if (d5 && SameDomainName(d5, n9)) return(mDNStrue); + if (d5 && SameDomainName(d5, nA)) return(mDNStrue); + if (d5 && SameDomainName(d5, nB)) return(mDNStrue); + return(mDNSfalse); +} + +mDNSexport const mDNSu8 *LastLabel(const domainname *d) +{ + const mDNSu8 *p = d->c; + while (d->c[0]) + { + p = d->c; + d = (const domainname*)(d->c + 1 + d->c[0]); + } + return(p); +} // Returns length of a domain name INCLUDING the byte for the final null label -// i.e. for the root label "." it returns one +// e.g. for the root label "." it returns one // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero) -// Legal results are 1 (just root label) to 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)); - } +// Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME) +// If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1) +mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit) +{ + const mDNSu8 *src = name->c; + while (src < limit && *src <= MAX_DOMAIN_LABEL) + { + if (*src == 0) return((mDNSu16)(src - name->c + 1)); + src += 1 + *src; + } + return(MAX_DOMAIN_NAME+1); +} // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte -// for the final null label i.e. for the root label "." it returns one. +// for the final null label, e.g. for the root label "." it returns one. // E.g. for the FQDN "foo.com." it returns 9 // (length, three data bytes, length, three more data bytes, final zero). // In the case where a parent domain name is provided, and the given name is a child @@ -643,121 +729,139 @@ mDNSexport mDNSu16 DomainNameLength(const domainname *const name) // 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)); - } +{ + const mDNSu8 *src = name->c; + if (parent && parent->c[0] == 0) parent = mDNSNULL; + while (*src) + { + if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); + if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2)); + src += 1 + *src; + if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); + } + return((mDNSu16)(src - name->c + 1)); +} + +// CountLabels() returns number of labels in name, excluding final root label +// (e.g. for "apple.com." CountLabels returns 2.) +mDNSexport int CountLabels(const domainname *d) +{ + int count = 0; + const mDNSu8 *ptr; + for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++; + return count; +} + +// SkipLeadingLabels skips over the first 'skip' labels in the domainname, +// returning a pointer to the suffix with 'skip' labels removed. +mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip) +{ + while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; } + return(d); +} // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname. // The C string contains the label as-is, with no escaping, etc. // Any dots in the name are literal dots, not label separators // If successful, AppendLiteralLabelString returns a pointer to the next unused byte -// in the domainname bufer (i.e., the next byte after the terminating zero). -// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// in the domainname bufer (i.e. the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) // AppendLiteralLabelString returns mDNSNULL. mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr) - { - mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name - const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) - const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL; - const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2; - mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go - - while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data - *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte - *ptr++ = 0; // Put the null root label on the end - if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input - else return(ptr); // Success: return new value of ptr - } +{ + 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) +// in the domainname bufer (i.e. the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) // AppendDNSNameString returns mDNSNULL. mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring) - { - const char *cstr = cstring; - mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name - const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) - while (*cstr && ptr < lim) // While more characters, and space to put them... - { - mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go - if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); } - while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... - { - mDNSu8 c = (mDNSu8)*cstr++; // Read the character - if (c == '\\') // If escape character, check next character - { - c = (mDNSu8)*cstr++; // Assume we'll just take the next character - if (mdnsIsDigit(cstr[-1]) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1])) - { // If three decimal digits, - int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal - int v1 = cstr[ 0] - '0'; - int v2 = cstr[ 1] - '0'; - int val = v0 * 100 + v1 * 10 + v2; - if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it - } - } - *ptr++ = c; // Write the character - } - if (*cstr) cstr++; // Skip over the trailing dot (if present) - if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort - return(mDNSNULL); - *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte - } - - *ptr++ = 0; // Put the null root label on the end - if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input - else return(ptr); // Success: return new value of ptr - } +{ + 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) +// in the domainname bufer (i.e. the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) // AppendDomainLabel returns mDNSNULL. mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label) - { - int i; - mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; +{ + int i; + mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; - // Check label is legal - if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL); + // 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); + // 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); - } + 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); - } +{ + 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. @@ -766,74 +870,74 @@ mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *co // 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 - } +{ + 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) +// in the domainname bufer (i.e. the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) // MakeDomainNameFromDNSNameString returns mDNSNULL. mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr) - { - name->c[0] = 0; // Make an empty domain name - return(AppendDNSNameString(name, cstr)); // And then add this string to it - } +{ + 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) +{ + const mDNSu8 * src = label->c; // Domain label we're reading + const mDNSu8 len = *src++; // Read length of this (non-null) label + const mDNSu8 *const end = src + len; // Work out where the label ends + if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort + while (src < end) // While we have characters in the label + { + mDNSu8 c = *src++; + if (esc) + { + if (c == '.' || c == esc) // If character is a dot or the escape character + *ptr++ = esc; // Output escape character + else if (c <= ' ') // If non-printing ascii, + { // Output decimal escape sequence + *ptr++ = esc; + *ptr++ = (char) ('0' + (c / 100) ); + *ptr++ = (char) ('0' + (c / 10) % 10); + c = (mDNSu8)('0' + (c ) % 10); + } + } + *ptr++ = (char)c; // Copy the character + } + *ptr = 0; // Null-terminate the string + return(ptr); // and return +} + +// Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes) mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) - { - const mDNSu8 *src = name->c; // Domain name we're reading - const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid +{ + 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 + 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 - } + 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 - } + *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, @@ -841,110 +945,145 @@ mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const n // 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]); - } +{ + const mDNSu8 * src = &UTF8Name[1]; + const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0]; + mDNSu8 * ptr = &hostlabel->c[1]; + const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL; + while (src < end) + { + // Delete apostrophes from source name + if (src[0] == '\'') { src++; continue; } // Standard straight single quote + if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99) + { src += 3; continue; } // Unicode curly apostrophe + if (ptr < lim) + { + if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src; + else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-'; + } + src++; + } + while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks + hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]); +} + +#define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \ + ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \ + ((X)[4] | 0x20) == 'p') mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, - const domainlabel *name, const domainname *type, const domainname *const domain) - { - int i, len; - mDNSu8 *dst = fqdn->c; - const mDNSu8 *src; - const char *errormsg; - - // 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); + const domainlabel *name, const domainname *type, const domainname *const domain) +{ + int i, len; + mDNSu8 *dst = fqdn->c; + const mDNSu8 *src; + const char *errormsg; +#if APPLE_OSX_mDNSResponder + mDNSBool loggedUnderscore = mDNSfalse; + static char typeBuf[MAX_ESCAPED_DOMAIN_NAME]; +#endif + + // In the case where there is no name (and ONLY in that case), + // a single-label subtype is allowed as the first label of a three-part "type" + if (!name && type) + { + const mDNSu8 *s0 = type->c; + if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) + { + const mDNSu8 * s1 = s0 + 1 + s0[0]; + if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63) + { + const mDNSu8 *s2 = s1 + 1 + s1[0]; + if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels + { + static const mDNSu8 SubTypeLabel[5] = mDNSSubTypeLabel; + src = s0; // Copy the first label + len = *src; + for (i=0; i <= len; i++) *dst++ = *src++; + for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i]; + type = (const domainname *)s1; + + // Special support to enable the DNSServiceBrowse call made by Bonjour Browser + // For these queries, we retract the "._sub" we just added between the subtype and the main type + // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse + if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) + dst -= sizeof(SubTypeLabel); + } + } + } + } + + if (name && name->c[0]) + { + src = name->c; // Put the service name into the domain name + len = *src; + if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; } + for (i=0; i<=len; i++) *dst++ = *src++; + } + else + name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below + + src = type->c; // Put the service type into the domain name + len = *src; + if (len < 2 || len > 16) + { + LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. " + "See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c); +#if APPLE_OSX_mDNSResponder + ConvertDomainNameToCString(type, typeBuf); + mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, ""); +#endif + } + if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL); + if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; } + for (i=2; i<=len; i++) + { + // Letters and digits are allowed anywhere + if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue; + // Hyphens are only allowed as interior characters + // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them, + // with the same rule as hyphens + if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len) + { +#if APPLE_OSX_mDNSResponder + if (src[i] == '_' && loggedUnderscore == mDNSfalse) + { + ConvertDomainNameToCString(type, typeBuf); + mDNSASLLog(mDNSNULL, "serviceType.nameWithUnderscore", "noop", typeBuf, ""); + loggedUnderscore = mDNStrue; + } +#endif + continue; + } + errormsg = "Application protocol name must contain only letters, digits, and hyphens"; +#if APPLE_OSX_mDNSResponder + { + ConvertDomainNameToCString(type, typeBuf); + mDNSASLLog(mDNSNULL, "serviceType.nameWithIllegalCharacters", "noop", typeBuf, ""); + } +#endif + goto fail; + } + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; } + for (i=0; i<=len; i++) *dst++ = *src++; + + if (*src) { errormsg = "Service type must have only two labels"; goto fail; } + + *dst = 0; + if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; } + if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa")) + { errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; } + dst = AppendDomainName(fqdn, domain); + if (!dst) { errormsg = "Service domain too long"; goto fail; } + return(dst); fail: - LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c); - return(mDNSNULL); - } + 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 @@ -952,45 +1091,124 @@ fail: // 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); - } + domainlabel *const name, domainname *const type, domainname *const domain) +{ + int i, len; + const mDNSu8 *src = fqdn->c; + const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME; + mDNSu8 *dst; + + dst = name->c; // Extract the service name + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); } + if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + + dst = type->c; // Extract the service type + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); } + if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); } + if (src[1] != '_') { debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); } + if (!ValidTransportProtocol(src)) + { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + *dst++ = 0; // Put terminator on the end of service type + + dst = domain->c; // Extract the service domain + while (*src) + { + len = *src; + if (len >= 0x40) + { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); } + if (src + 1 + len + 1 >= max) + { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + } + *dst++ = 0; // Put the null root label on the end + + return(mDNStrue); +} + +mDNSexport mStatus DNSNameToLowerCase(domainname *d, domainname *result) +{ + const mDNSu8 *a = d->c; + mDNSu8 *b = result->c; + const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME; + int i, len; + + while (*a) + { + if (a + 1 + *a >= max) + { + LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name"); + return mStatus_BadParamErr; + } + len = *a++; + *b++ = len; + for (i = 0; i < len; i++) + { + mDNSu8 ac = *a++; + if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; + *b++ = ac; + } + } + *b = 0; + + return mStatus_NoError; +} + +mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen, + const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen) +{ + AlgContext *ctx; + int i; + domainname lname; + mDNSu8 *p = (mDNSu8 *)&nsec3->salt; + const mDNSu8 *digest; + int digestlen; + mDNSBool first = mDNStrue; + + if (DNSNameToLowerCase((domainname *)name, &lname) != mStatus_NoError) + { + LogMsg("NSEC3HashName: ERROR!! DNSNameToLowerCase failed"); + return mDNSNULL; + } + + digest = lname.c; + digestlen = DomainNameLength(&lname); + + // Note that it is "i <=". The first iteration is for digesting the name and salt. + // The iteration count does not include that. + for (i = 0; i <= swap16(nsec3->iterations); i++) + { + ctx = AlgCreate(DIGEST_ALG, nsec3->alg); + if (!ctx) + { + LogMsg("NSEC3HashName: ERROR!! Cannot allocate context"); + return mDNSNULL; + } + + AlgAdd(ctx, digest, digestlen); + if (nsec3->saltLength) + AlgAdd(ctx, p, nsec3->saltLength); + if (AnonDataLen) + AlgAdd(ctx, AnonData, AnonDataLen); + if (first) + { + first = mDNSfalse; + digest = hash; + digestlen = AlgLength(ctx); + } + AlgFinal(ctx, (void *)digest, digestlen); + AlgDestroy(ctx); + } + *dlen = digestlen; + return digest; +} // Notes on UTF-8: // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F @@ -1008,125 +1226,125 @@ mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn, // 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); - } +{ + if (length > max) + { + mDNSu8 c1 = string[max]; // First byte after cut point + mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point + length = max; // Trim length down + while (length > 0) + { + // Check if the byte right after the chop point is a UTF-8 continuation byte, + // or if the character right after the chop point is the second of a UTF-16 surrogate pair. + // If so, then we continue to chop more bytes until we get to a legal chop point. + mDNSBool continuation = ((c1 & 0xC0) == 0x80); + mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0); + if (!continuation && !secondsurrogate) break; + c2 = c1; + c1 = string[--length]; + } + // Having truncated characters off the end of our string, also cut off any residual white space + while (length > 0 && string[length-1] <= ' ') length--; + } + return(length); +} // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034 // name ends in "-nnn", where n is some decimal number. mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText) - { - mDNSu16 l = name->c[0]; - - if (RichText) - { - if (l < 4) return mDNSfalse; // Need at least " (2)" - if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')' - if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit - l--; - while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits - return (name->c[l] == '(' && name->c[l - 1] == ' '); - } - else - { - if (l < 2) return mDNSfalse; // Need at least "-2" - if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit - l--; - while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits - return (name->c[l] == '-'); - } - } +{ + 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; +{ + 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]--; + // 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]--; } + // 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; - } + // 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); - } + 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)") +mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText) +{ + mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2") + if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)") - // Truncate trailing spaces from RichText names - if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--; + // Truncate trailing spaces from RichText names + if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--; - while (val >= divisor * 10) { divisor *= 10; chars++; } + while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; } - name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars); + 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]] = '-'; } + 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; - } + while (divisor) + { + name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor); + val %= divisor; + divisor /= 10; + } - if (RichText) name->c[++name->c[0]] = ')'; - } + if (RichText) name->c[++name->c[0]] = ')'; +} mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) - { - mDNSu32 val = 0; +{ + mDNSu32 val = 0; - if (LabelContainsSuffix(name, RichText)) - val = RemoveLabelSuffix(name, RichText); + 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); + // 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); - } + AppendLabelSuffix(name, val, RichText); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1134,196 +1352,794 @@ mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) #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); - } +// Set up a AuthRecord with sensible default values. +// These defaults may be overwritten with new values before mDNS_Register is called +mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, + mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context) +{ + // + // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID. + // Most of the applications normally create with LocalOnly InterfaceID and we store them as + // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID. + // LocalOnly resource records can also be created with valid InterfaceID which happens today + // when we create LocalOnly records for /etc/hosts. + + if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly)) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + + // Don't try to store a TTL bigger than we can represent in platform time units + if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) + ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; + else if (ttl == 0) // And Zero TTL is illegal + ttl = DefaultTTLforRRType(rrtype); + + // Field Group 1: The actual information pertaining to this resource record + rr->resrec.RecordType = RecordType; + rr->resrec.InterfaceID = InterfaceID; + rr->resrec.name = &rr->namestorage; + rr->resrec.rrtype = rrtype; + rr->resrec.rrclass = kDNSClass_IN; + rr->resrec.rroriginalttl = ttl; + rr->resrec.rDNSServer = mDNSNULL; + rr->resrec.AnonInfo = mDNSNULL; +// rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal +// rr->resrec.rdestimate = set in mDNS_Register_internal +// rr->resrec.rdata = MUST be set by client + + if (RDataStorage) + rr->resrec.rdata = RDataStorage; + else + { + rr->resrec.rdata = &rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); + } + + // Field Group 2: Persistent metadata for Authoritative Records + rr->Additional1 = mDNSNULL; + rr->Additional2 = mDNSNULL; + rr->DependentOn = mDNSNULL; + rr->RRSet = mDNSNULL; + rr->RecordCallback = Callback; + rr->RecordContext = Context; + + rr->AutoTarget = Target_Manual; + rr->AllowRemoteQuery = mDNSfalse; + rr->ForceMCast = mDNSfalse; + + rr->WakeUp = zeroOwner; + rr->AddressProxy = zeroAddr; + rr->TimeRcvd = 0; + rr->TimeExpire = 0; + rr->ARType = artype; + rr->AuthFlags = 0; + + // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal) + // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal) + + // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case + // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch + // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.) + rr->state = regState_Zero; + rr->uselease = 0; + rr->expire = 0; + rr->Private = 0; + rr->updateid = zeroID; + rr->zone = rr->resrec.name; + rr->nta = mDNSNULL; + rr->tcp = mDNSNULL; + rr->OrigRData = 0; + rr->OrigRDLen = 0; + rr->InFlightRData = 0; + rr->InFlightRDLen = 0; + rr->QueuedRData = 0; + rr->QueuedRDLen = 0; + mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); + rr->SRVChanged = mDNSfalse; + rr->mState = mergeState_Zero; + + rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register() +} + +mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name, + const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context) +{ + q->InterfaceID = InterfaceID; + q->flags = 0; + q->Target = zeroAddr; + AssignDomainName(&q->qname, name); + q->qtype = qtype; + q->qclass = kDNSClass_IN; + q->LongLived = (qtype == kDNSType_PTR); + q->ExpectUnique = (qtype != kDNSType_PTR); + q->ForceMCast = mDNSfalse; + q->ReturnIntermed = mDNSfalse; + q->SuppressUnusable = mDNSfalse; + q->DenyOnCellInterface = mDNSfalse; + q->DenyOnExpInterface = mDNSfalse; + q->SearchListIndex = 0; + q->AppendSearchDomains = 0; + q->RetryWithSearchDomains = mDNSfalse; + q->TimeoutQuestion = 0; + q->WakeOnResolve = 0; + q->UseBackgroundTrafficClass = mDNSfalse; + q->ValidationRequired = 0; + q->ValidatingResponse = 0; + q->ProxyQuestion = 0; + q->qnameOrig = mDNSNULL; + q->AnonInfo = mDNSNULL; + q->pid = mDNSPlatformGetPID(); + q->DisallowPID = mDNSfalse; + q->ServiceID = -1; + q->QuestionCallback = callback; + q->QuestionContext = context; +} + +mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr) +{ + int len = rr->rdlength; + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + const mDNSu8 *ptr = rdb->data; + mDNSu32 sum = 0; + + switch(rr->rrtype) + { + case kDNSType_NS: + case kDNSType_MD: + case kDNSType_MF: + case kDNSType_CNAME: + case kDNSType_MB: + case kDNSType_MG: + case kDNSType_MR: + case kDNSType_PTR: + case kDNSType_NSAP_PTR: + case kDNSType_DNAME: return DomainNameHashValue(&rdb->name); + + case kDNSType_SOA: return rdb->soa.serial + + rdb->soa.refresh + + rdb->soa.retry + + rdb->soa.expire + + rdb->soa.min + + DomainNameHashValue(&rdb->soa.mname) + + DomainNameHashValue(&rdb->soa.rname); + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: return DomainNameHashValue(&rdb->mx.exchange); + + case kDNSType_MINFO: + case kDNSType_RP: return DomainNameHashValue(&rdb->rp.mbox) + DomainNameHashValue(&rdb->rp.txt); + + case kDNSType_PX: return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400); + + case kDNSType_SRV: return DomainNameHashValue(&rdb->srv.target); + + case kDNSType_OPT: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare + + case kDNSType_NSEC: { + int dlen; + dlen = DomainNameLength((domainname *)rdb->data); + sum = DomainNameHashValue((domainname *)rdb->data); + ptr += dlen; + len -= dlen; + /* FALLTHROUGH */ + } + + default: + { + int i; + for (i=0; i+1 < len; i+=2) + { + sum += (((mDNSu32)(ptr[i])) << 8) | ptr[i+1]; + sum = (sum<<3) | (sum>>29); + } + if (i < len) + { + sum += ((mDNSu32)(ptr[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 SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename) +{ + const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data; + const RDataBody2 *const b2 = (RDataBody2 *)r2; + switch(r1->rrtype) + { + case kDNSType_NS: + case kDNSType_MD: + case kDNSType_MF: + case kDNSType_CNAME: + case kDNSType_MB: + case kDNSType_MG: + case kDNSType_MR: + case kDNSType_PTR: + case kDNSType_NSAP_PTR: + case kDNSType_DNAME: return(SameDomainName(&b1->name, &b2->name)); + + case kDNSType_SOA: return (mDNSBool)( b1->soa.serial == b2->soa.serial && + b1->soa.refresh == b2->soa.refresh && + b1->soa.retry == b2->soa.retry && + b1->soa.expire == b2->soa.expire && + b1->soa.min == b2->soa.min && + samename(&b1->soa.mname, &b2->soa.mname) && + samename(&b1->soa.rname, &b2->soa.rname)); + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: return (mDNSBool)( b1->mx.preference == b2->mx.preference && + samename(&b1->mx.exchange, &b2->mx.exchange)); + + case kDNSType_MINFO: + case kDNSType_RP: return (mDNSBool)( samename(&b1->rp.mbox, &b2->rp.mbox) && + samename(&b1->rp.txt, &b2->rp.txt)); + + case kDNSType_PX: return (mDNSBool)( b1->px.preference == b2->px.preference && + samename(&b1->px.map822, &b2->px.map822) && + samename(&b1->px.mapx400, &b2->px.mapx400)); + + case kDNSType_SRV: return (mDNSBool)( b1->srv.priority == b2->srv.priority && + b1->srv.weight == b2->srv.weight && + mDNSSameIPPort(b1->srv.port, b2->srv.port) && + samename(&b1->srv.target, &b2->srv.target)); + + case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare + case kDNSType_NSEC: { + // If the "nxt" name changes in case, we want to delete the old + // and store just the new one. If the caller passes in SameDomainCS for "samename", + // we would return "false" when the only change between the two rdata is the case + // change in "nxt". + // + // Note: rdlength of both the RData are same (ensured by the caller) and hence we can + // use just r1->rdlength below + + int dlen1 = DomainNameLength((domainname *)b1->data); + int dlen2 = DomainNameLength((domainname *)b2->data); + return (mDNSBool)(dlen1 == dlen2 && + samename((domainname *)b1->data, (domainname *)b2->data) && + mDNSPlatformMemSame(b1->data + dlen1, b2->data + dlen2, r1->rdlength - dlen1)); + } + + default: return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength)); + } +} + +mDNSexport mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type) +{ + int win, wlen; + int wintype; + + // The window that this type belongs to. NSEC has 256 windows that + // comprises of 256 types. + wintype = type >> 8; + + while (bitmaplen > 0) + { + if (bitmaplen < 3) + { + LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d short", bitmaplen); + return mDNSfalse; + } + + win = *bmap++; + wlen = *bmap++; + bitmaplen -= 2; + if (bitmaplen < wlen || wlen < 1 || wlen > 32) + { + LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win); + return mDNSfalse; + } + if (win < 0 || win >= 256) + { + LogInfo("BitmapTypeCheck: malformed nsec, wlen %d", wlen); + return mDNSfalse; + } + if (win == wintype) + { + // First byte in the window serves 0 to 7, the next one serves 8 to 15 and so on. + // Calculate the right byte offset first. + int boff = (type & 0xff ) >> 3; + if (wlen <= boff) + return mDNSfalse; + // The last three bits values 0 to 7 corresponds to bit positions + // within the byte. + return (bmap[boff] & (0x80 >> (type & 7))); + } + else + { + // If the windows are ordered, then we could check to see + // if wintype > win and then return early. + bmap += wlen; + bitmaplen -= wlen; + } + } + return mDNSfalse; +} + +// Don't call this function if the resource record is not NSEC. It will return false +// which means that the type does not exist. +mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type) +{ + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + mDNSu8 *nsec = (mDNSu8 *)rdb->data; + int len, bitmaplen; + mDNSu8 *bmap; + + if (rr->rrtype != kDNSType_NSEC) return mDNSfalse; + + len = DomainNameLength((domainname *)nsec); + + bitmaplen = rr->rdlength - len; + bmap = nsec + len; + return (BitmapTypeCheck(bmap, bitmaplen, type)); +} + +// Don't call this function if the resource record is not NSEC. It will return false +// which means that the type exists. +mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type) +{ + if (rr->rrtype != kDNSType_NSEC) return mDNSfalse; + + return !RRAssertsExistence(rr, type); +} + +// Checks whether the RRSIG or NSEC record answers the question "q". +mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q, mDNSBool *checkType) +{ + *checkType = mDNStrue; + + // This function is called for all questions and as long as the type matches, + // return true. For the types (RRSIG and NSEC) that are specifically checked in + // this function, returning true still holds good. + if (q->qtype == rr->rrtype) + return mDNStrue; + + // For DS and DNSKEY questions, the types should match i.e., don't answer using CNAME + // records as it answers any question type. + // + // - DS record comes from the parent zone where CNAME record cannot coexist and hence + // cannot possibly answer it. + // + // - For DNSKEY, one could potentially follow CNAME but there could be a DNSKEY at + // the "qname" itself. To keep it simple, we don't follow CNAME. + + if ((q->qtype == kDNSType_DS || q->qtype == kDNSType_DNSKEY) && (q->qtype != rr->rrtype)) + { + debugf("DNSSECRecordAnswersQuestion: %d type resource record matched question %##s (%s), ignoring", rr->rrtype, + q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + + // If we are validating a response using DNSSEC, we might already have the records + // for the "q->qtype" in the cache but we issued a query with DO bit set + // to get the RRSIGs e.g., if you have two questions one of which does not require + // DNSSEC validation. When the RRSIG is added to the cache, we need to deliver + // the response to the question. The RRSIG type won't match the q->qtype and hence + // we need to bypass the check in that case. + if (rr->rrtype == kDNSType_RRSIG && q->ValidatingResponse) + { + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + rdataRRSig *rrsig = (rdataRRSig *)rdb->data; + mDNSu16 typeCovered = swap16(rrsig->typeCovered); + debugf("DNSSECRecordAnswersQuestion: Matching RRSIG typeCovered %s", DNSTypeName(typeCovered)); + if (typeCovered != kDNSType_CNAME && typeCovered != q->qtype) + { + debugf("DNSSECRecordAnswersQuestion: RRSIG did not match question %##s (%s)", q->qname.c, + DNSTypeName(q->qtype)); + return mDNSfalse; + } + LogInfo("DNSSECRecordAnswersQuestion: RRSIG matched question %##s (%s)", q->qname.c, + DNSTypeName(q->qtype)); + *checkType = mDNSfalse; + return mDNStrue; + } + // If the NSEC record asserts the non-existence of a name looked up by the question, we would + // typically answer that e.g., the bitmap asserts that q->qtype does not exist. If we have + // to prove the non-existence as required by ValidatingResponse and ValidationRequired question, + // then we should not answer that as it may not be the right one always. We may need more than + // one NSEC to prove the non-existence. + if (rr->rrtype == kDNSType_NSEC && DNSSECQuestion(q)) + { + debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (NSEC)", q->qname.c, + DNSTypeName(q->qtype), rr->name->c); + return mDNSfalse; + } + return mDNStrue; +} + +// ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question. +// SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call. +// SameDomainName() is generally cheap when the names don't match, but expensive when they do match, +// because it has to check all the way to the end of the names to be sure. +// In cases where we know in advance that the names match it's especially advantageous to skip the +// SameDomainName() call because that's precisely the time when it's most expensive and least useful. + +mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + mDNSBool checkType = mDNStrue; + + // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records + // are handled in LocalOnlyRecordAnswersQuestion + if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) + { + LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); + return mDNSfalse; + } + if (QuerySuppressed(q)) + return mDNSfalse; + + if (rr->InterfaceID && + q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && + rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // Resource record received via unicast, the resolver group ID should match ? + if (!rr->InterfaceID) + { + mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0); + mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0); + if (idr != idq) return(mDNSfalse); + if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse; + } + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question + if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + + // CNAME answers question of any type and a negative cache record should not prevent us from querying other + // valid types at the same name. + if (rr->rrtype == kDNSType_CNAME && rr->RecordType == kDNSRecordTypePacketNegative && rr->rrtype != q->qtype) + return mDNSfalse; + + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. + if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + +#if APPLE_OSX_mDNSResponder + if (!mDNSPlatformValidRecordForQuestion(rr, q)) + return mDNSfalse; +#endif // APPLE_OSX_mDNSResponder + + if (!AnonInfoAnswersQuestion(rr, q)) + return mDNSfalse; + + return(mDNStrue); +} 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)); - } +{ + if (!SameNameRecordAnswersQuestion(rr, q)) + return mDNSfalse; + + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); +} + +// We have a separate function to handle LocalOnly AuthRecords because they can be created with +// a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike +// multicast resource records (which has a valid InterfaceID) which can't be used to answer +// unicast questions. ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion can't tell whether +// a resource record is multicast or LocalOnly by just looking at the ResourceRecord because +// LocalOnly records are truly identified by ARType in the AuthRecord. As P2P and LocalOnly record +// are kept in the same hash table, we use the same function to make it easy for the callers when +// they walk the hash table to answer LocalOnly/P2P questions +// +mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q) +{ + ResourceRecord *rr = &ar->resrec; + + // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any + // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion + if (RRAny(ar)) + { + LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c); + return mDNSfalse; + } + + // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are + // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly, + // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against + // the InterfaceID in the resource record. + // + // mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any. + + if (rr->InterfaceID && + q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast && + rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records + // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set + // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped). + // + // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record. + // + // 2) Question: Any, LocalOnly Record: scoped. This question should be answered with the record because + // traditionally applications never specify scope e.g., getaddrinfo, but need to be able + // to get to /etc/hosts entries. + // + // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2). + // If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a + // non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two + // cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so. + // + // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be + // answered with any resource record where as if it has a valid InterfaceID, the scope should match. + // + // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL + // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record + // against the question. + // + // For P2P, InterfaceIDs of the question and the record should match. + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. + // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries. + // We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then + // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records + // with names that don't end in local and have mDNSInterface_LocalOnly set. + // + // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for + // a question to match its names, it also has to end in .local and that question can't be a unicast question (See + // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check + // and also makes it future proof. + + if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. + if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + + if (!AnonInfoAnswersQuestion(rr, q)) + return mDNSfalse; + + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); +} + +mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records + // are handled in LocalOnlyRecordAnswersQuestion + if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) + { + LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); + return mDNSfalse; + } + if (rr->InterfaceID && + q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && + rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // Resource record received via unicast, the resolver group ID should match ? + // Note that Auth Records are normally setup with NULL InterfaceID and + // both the DNSServers are assumed to be NULL in that case + if (!rr->InterfaceID) + { + mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0); + mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0); + if (idr != idq) return(mDNSfalse); + } + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question + if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + + if (!AnonInfoAnswersQuestion(rr, q)) + return mDNSfalse; + + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); +} + +// This is called with both unicast resource record and multicast resource record. The question that +// received the unicast response could be the regular unicast response from a DNS server or a response +// to a mDNS QU query. The main reason we need this function is that we can't compare DNSServers between the +// question and the resource record because the resource record is not completely initialized in +// mDNSCoreReceiveResponse when this function is called. +mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + mDNSBool checkType = mDNStrue; + + if (QuerySuppressed(q)) + return mDNSfalse; + + // For resource records created using multicast, the InterfaceIDs have to match + if (rr->InterfaceID && + q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. + if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + + if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse; + + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. + if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); + + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); +} mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) - { - const 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); - } - } - +{ + const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data; + const domainname *const name = estimate ? rr->name : mDNSNULL; + if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136) + else switch (rr->rrtype) + { + case kDNSType_A: return(sizeof(rd->ipv4)); + + case kDNSType_NS: + case kDNSType_CNAME: + case kDNSType_PTR: + case kDNSType_DNAME: return(CompressedDomainNameLength(&rd->name, name)); + + case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) + + CompressedDomainNameLength(&rd->soa.rname, name) + + 5 * sizeof(mDNSOpaque32)); + + case kDNSType_NULL: + case kDNSType_TSIG: + case kDNSType_TXT: + case kDNSType_X25: + case kDNSType_ISDN: + case kDNSType_LOC: + case kDNSType_DHCID: return(rr->rdlength); // Not self-describing, so have to just trust rdlength + + case kDNSType_HINFO: return (mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]); + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name)); + + case kDNSType_RP: return (mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) + + CompressedDomainNameLength(&rd->rp.txt, name)); + + case kDNSType_PX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) + + CompressedDomainNameLength(&rd->px.mapx400, name)); + + case kDNSType_AAAA: return(sizeof(rd->ipv6)); + + case kDNSType_SRV: return (mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name)); + + case kDNSType_OPT: return(rr->rdlength); + + case kDNSType_NSEC: { + domainname *next = (domainname *)rd->data; + int dlen = DomainNameLength(next); + // + if (UNICAST_NSEC(rr)) + return (mDNSu16)(CompressedDomainNameLength(next, name) + rr->rdlength - dlen); + else + return (mDNSu16)((estimate ? 2 : dlen) + rr->rdlength - dlen); + } + + default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); + return(rr->rdlength); + } +} + +// When a local client registers (or updates) a record, we use this routine to do some simple validation checks +// to help reduce the risk of bogus malformed data on the network mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd) - { - mDNSu16 len; - - switch(rrtype) - { - case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); - - case kDNSType_NS: // Same as PTR - case kDNSType_MD: // Same as PTR - case kDNSType_MF: // Same as PTR - case kDNSType_CNAME:// Same as PTR - //case kDNSType_SOA not checked - case kDNSType_MB: // Same as PTR - case kDNSType_MG: // Same as PTR - case kDNSType_MR: // Same as PTR - //case kDNSType_NULL not checked (no specified format, so always valid) - //case kDNSType_WKS not checked - case kDNSType_PTR: 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 - } - } +{ + mDNSu16 len; + + switch(rrtype) + { + case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); + + case kDNSType_NS: // Same as PTR + case kDNSType_MD: // Same as PTR + case kDNSType_MF: // Same as PTR + case kDNSType_CNAME: // Same as PTR + //case kDNSType_SOA not checked + case kDNSType_MB: // Same as PTR + case kDNSType_MG: // Same as PTR + case kDNSType_MR: // Same as PTR + //case kDNSType_NULL not checked (no specified format, so always valid) + //case kDNSType_WKS not checked + case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength); + return(len <= MAX_DOMAIN_NAME && rdlength == len); + + case kDNSType_HINFO: // Same as TXT (roughly) + case kDNSType_MINFO: // Same as TXT (roughly) + case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035) + { + const mDNSu8 *ptr = rd->u.txt.c; + const mDNSu8 *end = rd->u.txt.c + rdlength; + while (ptr < end) ptr += 1 + ptr[0]; + return (ptr == end); + } + + case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr)); + + case kDNSType_MX: // Must be at least two-byte preference, plus domainname + // Call to DomainNameLengthLimit() implicitly enforces both requirements for us + len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength); + return(len <= MAX_DOMAIN_NAME && rdlength == 2+len); + + case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname + // Call to DomainNameLengthLimit() implicitly enforces both requirements for us + len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength); + return(len <= MAX_DOMAIN_NAME && rdlength == 6+len); + + //case kDNSType_NSEC not checked + + default: return(mDNStrue); // Allow all other types without checking + } +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - -#pragma mark - #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; - } +{ + 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); - } +{ + 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) @@ -1333,389 +2149,534 @@ mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const // 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); - } + mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name) +{ + const mDNSu8 *const base = (const mDNSu8 *)msg; + const mDNSu8 * np = name->c; + const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid + const mDNSu8 * pointer = mDNSNULL; + const mDNSu8 *const searchlimit = ptr; + + if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); } + + if (!*np) // If just writing one-byte root label, make sure we have space for that + { + if (ptr >= limit) return(mDNSNULL); + } + else // else, loop through writing labels and/or a compression offset + { + do { + if (*np > MAX_DOMAIN_LABEL) + { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); } + + // This check correctly allows for the final trailing root label: + // e.g. + // Suppose our domain name is exactly 256 bytes long, including the final trailing root label. + // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local"). + // We know that max will be at name->c[256] + // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our + // six bytes, then exit the loop, write the final terminating root label, and the domain + // name we've written is exactly 256 bytes long, exactly at the correct legal limit. + // If the name is one byte longer, then we fail the "if" test below, and correctly bail out. + if (np + 1 + *np >= max) + { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); } + + if (base) pointer = FindCompressionPointer(base, searchlimit, np); + if (pointer) // Use a compression pointer if we can + { + const mDNSu16 offset = (mDNSu16)(pointer - base); + if (ptr+2 > limit) return(mDNSNULL); // If we don't have two bytes of space left, give up + *ptr++ = (mDNSu8)(0xC0 | (offset >> 8)); + *ptr++ = (mDNSu8)( offset & 0xFF); + return(ptr); + } + else // Else copy one label and try again + { + int i; + mDNSu8 len = *np++; + // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up + if (ptr + 1 + len >= limit) return(mDNSNULL); + *ptr++ = len; + for (i=0; i<len; i++) *ptr++ = *np++; + } + } while (*np); // While we've got characters remaining in the name, continue + } + + *ptr++ = 0; // Put the final root label + return(ptr); +} mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) - { - ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); - ptr[1] = (mDNSu8)((val ) & 0xFF); - return ptr + sizeof(mDNSOpaque16); - } +{ + 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 { 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); - } - } +{ + 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); +} + +// Copy the RDATA information. The actual in memory storage for the data might be bigger than what the rdlength +// says. Hence, the only way to copy out the data from a resource record is to use putRData. +// msg points to the message we're building (pass mDNSNULL for "msg" if we don't want to use compression pointers) +mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr) +{ + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + switch (rr->rrtype) + { + case kDNSType_A: if (rr->rdlength != 4) + { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); } + if (ptr + 4 > limit) return(mDNSNULL); + *ptr++ = rdb->ipv4.b[0]; + *ptr++ = rdb->ipv4.b[1]; + *ptr++ = rdb->ipv4.b[2]; + *ptr++ = rdb->ipv4.b[3]; + return(ptr); + + case kDNSType_NS: + case kDNSType_CNAME: + case kDNSType_PTR: + case kDNSType_DNAME: return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name)); + + case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname); + if (!ptr) return(mDNSNULL); + ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname); + if (!ptr || ptr + 20 > limit) return(mDNSNULL); + ptr = putVal32(ptr, rdb->soa.serial); + ptr = putVal32(ptr, rdb->soa.refresh); + ptr = putVal32(ptr, rdb->soa.retry); + ptr = putVal32(ptr, rdb->soa.expire); + ptr = putVal32(ptr, rdb->soa.min); + return(ptr); + + case kDNSType_NULL: + case kDNSType_HINFO: + case kDNSType_TSIG: + case kDNSType_TXT: + case kDNSType_X25: + case kDNSType_ISDN: + case kDNSType_LOC: + case kDNSType_DHCID: if (ptr + rr->rdlength > limit) return(mDNSNULL); + mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); + return(ptr + rr->rdlength); + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL); + ptr = putVal16(ptr, rdb->mx.preference); + return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange)); + + case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox); + if (!ptr) return(mDNSNULL); + ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt); + return(ptr); + + case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL); + ptr = putVal16(ptr, rdb->px.preference); + ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822); + if (!ptr) return(mDNSNULL); + ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400); + return(ptr); + + case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6)) + { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); } + if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL); + mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6)); + return(ptr + sizeof(rdb->ipv6)); + + case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL); + *ptr++ = (mDNSu8)(rdb->srv.priority >> 8); + *ptr++ = (mDNSu8)(rdb->srv.priority & 0xFF); + *ptr++ = (mDNSu8)(rdb->srv.weight >> 8); + *ptr++ = (mDNSu8)(rdb->srv.weight & 0xFF); + *ptr++ = rdb->srv.port.b[0]; + *ptr++ = rdb->srv.port.b[1]; + return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target)); + + case kDNSType_OPT: { + int len = 0; + const rdataOPT *opt; + const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength]; + for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) + len += DNSOpt_Data_Space(opt); + if (ptr + len > limit) + { + LogMsg("ERROR: putOptRData - out of space"); + return mDNSNULL; + } + for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) + { + const int space = DNSOpt_Data_Space(opt); + ptr = putVal16(ptr, opt->opt); + ptr = putVal16(ptr, (mDNSu16)space - 4); + switch (opt->opt) + { + case kDNSOpt_LLQ: + ptr = putVal16(ptr, opt->u.llq.vers); + ptr = putVal16(ptr, opt->u.llq.llqOp); + ptr = putVal16(ptr, opt->u.llq.err); + mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8); // 8-byte id + ptr += 8; + ptr = putVal32(ptr, opt->u.llq.llqlease); + break; + case kDNSOpt_Lease: + ptr = putVal32(ptr, opt->u.updatelease); + break; + case kDNSOpt_Owner: + *ptr++ = opt->u.owner.vers; + *ptr++ = opt->u.owner.seq; + mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6); // 6-byte Host identifier + ptr += 6; + if (space >= DNSOpt_OwnerData_ID_Wake_Space) + { + mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6); // 6-byte interface MAC + ptr += 6; + if (space > DNSOpt_OwnerData_ID_Wake_Space) + { + mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space); + ptr += space - DNSOpt_OwnerData_ID_Wake_Space; + } + } + break; + case kDNSOpt_Trace: + *ptr++ = opt->u.tracer.platf; + ptr = putVal32(ptr, opt->u.tracer.mDNSv); + break; + } + } + return ptr; + } + + case kDNSType_NSEC: { + // For NSEC records, rdlength represents the exact number of bytes + // of in memory storage. + int len = rr->rdlength; + mDNSu8 *nsec = (mDNSu8 *)rdb->data; + domainname *name = (domainname *)nsec; + int dlen; + + dlen = DomainNameLength(name); + len -= dlen; + nsec += dlen; + // This function is called when we are sending a NSEC record as part of mDNS, + // or to copy the data to any other buffer needed which could be a mDNS or uDNS + // NSEC record. The only time compression is used that when we are sending it + // in mDNS (indicated by non-NULL "msg") and hence we handle mDNS case + // separately. + if (!UNICAST_NSEC(rr)) + { + mDNSu8 *save = ptr; + int i, j, wlen; + wlen = *(nsec + 1); + nsec += 2; // Skip the window number and len + len -= 2; + + // For our simplified use of NSEC synthetic records: + // + // nextname is always the record's own name, + // the block number is always 0, + // the count byte is a value in the range 1-32, + // followed by the 1-32 data bytes + // + // Note: When we send the NSEC record in mDNS, the window size is set to 32. + // We need to find out what the last non-NULL byte is. If we are copying out + // from an RDATA, we have the right length. As we need to handle both the case, + // we loop to find the right value instead of blindly using len to copy. + + for (i=wlen; i>0; i--) if (nsec[i-1]) break; + + ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); + if (!ptr) { LogInfo("putRData: Can't put name, Length %d, record %##s", limit - save, rr->name->c); return(mDNSNULL); } + if (i) // Only put a block if at least one type exists for this name + { + if (ptr + 2 + i > limit) { LogInfo("putRData: Can't put window, Length %d, i %d, record %##s", limit - ptr, i, rr->name->c); return(mDNSNULL); } + *ptr++ = 0; + *ptr++ = (mDNSu8)i; + for (j=0; j<i; j++) *ptr++ = nsec[j]; + } + return ptr; + } + else + { + int win, wlen; + + // Sanity check whether the bitmap is good + while (len) + { + if (len < 3) + { LogMsg("putRData: invalid length %d", len); return mDNSNULL; } + + win = *nsec++; + wlen = *nsec++; + len -= 2; + if (len < wlen || wlen < 1 || wlen > 32) + { LogMsg("putRData: invalid window length %d", wlen); return mDNSNULL; } + if (win < 0 || win >= 256) + { LogMsg("putRData: invalid window %d", win); return mDNSNULL; } + + nsec += wlen; + len -= wlen; + } + if (ptr + rr->rdlength > limit) { LogMsg("putRData: NSEC rdlength beyond limit %##s (%s), ptr %p, rdlength %d, limit %p", rr->name->c, DNSTypeName(rr->rrtype), ptr, rr->rdlength, limit); return(mDNSNULL);} + + // No compression allowed for "nxt", just copy the data. + mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); + return(ptr + rr->rdlength); + } + } + + default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype); + if (ptr + rr->rdlength > limit) return(mDNSNULL); + mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); + return(ptr + rr->rdlength); + } +} + +#define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update) mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit) - { - mDNSu8 *endofrdata; - mDNSu16 actualLength; - - 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); - } +{ + mDNSu8 *endofrdata; + mDNSu16 actualLength; + // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782) + const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg; + + if (rr->RecordType == kDNSRecordTypeUnregistered) + { + LogMsg("PutResourceRecordTTLWithLimit ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + return(ptr); + } + + if (!ptr) + { + LogMsg("PutResourceRecordTTLWithLimit ptr is null %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + return(mDNSNULL); + } + + ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); + // If we're out-of-space, return mDNSNULL + if (!ptr || ptr + 10 >= limit) + { + LogInfo("PutResourceRecordTTLWithLimit: can't put name, out of space %##s (%s), ptr %p, limit %p", rr->name->c, + DNSTypeName(rr->rrtype), ptr, limit); + return(mDNSNULL); + } + ptr[0] = (mDNSu8)(rr->rrtype >> 8); + ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); + ptr[2] = (mDNSu8)(rr->rrclass >> 8); + ptr[3] = (mDNSu8)(rr->rrclass & 0xFF); + ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF); + ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF); + ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF); + ptr[7] = (mDNSu8)( ttl & 0xFF); + // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes + + endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr); + if (!endofrdata) + { + LogInfo("PutResourceRecordTTLWithLimit: Ran out of space in PutResourceRecord for %##s (%s), ptr %p, limit %p", rr->name->c, + DNSTypeName(rr->rrtype), ptr+10, limit); + return(mDNSNULL); + } + + // Go back and fill in the actual number of data bytes we wrote + // (actualLength can be less than rdlength when domain name compression is used) + actualLength = (mDNSu16)(endofrdata - ptr - 10); + ptr[8] = (mDNSu8)(actualLength >> 8); + ptr[9] = (mDNSu8)(actualLength & 0xFF); + + if (count) (*count)++; + else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + return(endofrdata); +} + +mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr) +{ + ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name); + if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type + ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF); + ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class + ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero + ptr[8] = ptr[9] = 0; // RDATA length is zero + (*count)++; + return(ptr + 10); +} mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass) - { - ptr = putDomainNameAsLabels(msg, ptr, limit, name); - if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL - ptr[0] = (mDNSu8)(rrtype >> 8); - ptr[1] = (mDNSu8)(rrtype & 0xFF); - ptr[2] = (mDNSu8)(rrclass >> 8); - ptr[3] = (mDNSu8)(rrclass & 0xFF); - msg->h.numQuestions++; - return(ptr+4); - } +{ + 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; - } +{ + 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; - } +mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end) +{ + AuthRecord prereq; + mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL); + AssignDomainName(&prereq.namestorage, name); + prereq.resrec.rrtype = kDNSQType_ANY; + prereq.resrec.rrclass = kDNSClass_NONE; + return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); +} // for dynamic updates mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr) - { - 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; - } +{ + // deletion: specify record w/ TTL 0, class NONE + const mDNSu16 origclass = rr->rrclass; + rr->rrclass = kDNSClass_NONE; + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0); + rr->rrclass = origclass; + return ptr; +} + +// for dynamic updates +mDNSexport mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit) +{ + // deletion: specify record w/ TTL 0, class NONE + const mDNSu16 origclass = rr->rrclass; + rr->rrclass = kDNSClass_NONE; + ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit); + rr->rrclass = origclass; + return ptr; +} + +mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit) +{ + mDNSu16 class = kDNSQClass_ANY; + + ptr = putDomainNameAsLabels(msg, ptr, limit, name); + if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rrtype >> 8); + ptr[1] = (mDNSu8)(rrtype & 0xFF); + ptr[2] = (mDNSu8)(class >> 8); + ptr[3] = (mDNSu8)(class & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl + ptr[8] = ptr[9] = 0; // zero rdlength/rdata + + msg->h.mDNS_numUpdates++; + return ptr + 10; +} // for dynamic updates mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name) - { - const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; - mDNSu16 class = kDNSQClass_ANY; - mDNSu16 rrtype = kDNSQType_ANY; - - ptr = putDomainNameAsLabels(msg, ptr, limit, name); - if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL - ptr[0] = (mDNSu8)(rrtype >> 8); - ptr[1] = (mDNSu8)(rrtype & 0xFF); - ptr[2] = (mDNSu8)(class >> 8); - ptr[3] = (mDNSu8)(class & 0xFF); - ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl - ptr[8] = ptr[9] = 0; // zero rdlength/rdata - - msg->h.mDNS_numUpdates++; - return ptr + 10; - } +{ + 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; - } +{ + AuthRecord rr; + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + rr.resrec.rrclass = NormalMaxDNSMessageData; + rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + rr.resrec.rdestimate = sizeof(rdataOPT); + rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + rr.resrec.rdata->u.opt[0].u.updatelease = lease; + end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } + return end; +} + +// for dynamic updates +mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit) +{ + AuthRecord rr; + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + rr.resrec.rrclass = NormalMaxDNSMessageData; + rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + rr.resrec.rdestimate = sizeof(rdataOPT); + rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + rr.resrec.rdata->u.opt[0].u.updatelease = lease; + end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } + return end; +} + +mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit) +{ + AuthRecord rr; + mDNSu32 ttl = 0; + + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + // It is still not clear what the right size is. We will have to fine tune this once we do + // a lot of testing with DNSSEC. + rr.resrec.rrclass = 4096; + rr.resrec.rdlength = 0; + rr.resrec.rdestimate = 0; + // set the DO bit + ttl |= 0x8000; + end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, ttl, limit); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } + return end; +} + +mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo, mDNSu8 *limit) +{ + if (authInfo && authInfo->AutoTunnel) + { + AuthRecord hinfo; + mDNSu8 *h = hinfo.rdatastorage.u.data; + mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0]; + mDNSu8 *newptr; + mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + AppendDomainLabel(&hinfo.namestorage, &m->hostlabel); + AppendDomainName (&hinfo.namestorage, &authInfo->domain); + hinfo.resrec.rroriginalttl = 0; + mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); + h += 1 + (int)h[0]; + mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); + hinfo.resrec.rdlength = len; + hinfo.resrec.rdestimate = len; + newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit); + return newptr; + } + else + return end; +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1724,372 +2685,1153 @@ mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) #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); - } +{ + 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); - } +{ + domainname *target; + if (NewRData) + { + rr->rdata = NewRData; + rr->rdlength = rdlength; + } + // Must not try to get target pointer until after updating rr->rdata + target = GetRRDomainNameTarget(rr); + rr->rdlength = GetRDLength(rr, mDNSfalse); + rr->rdestimate = GetRDLength(rr, mDNStrue); + rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr); +} mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end) - { - mDNSu16 total = 0; - - if (ptr < (mDNSu8*)msg || ptr >= end) - { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } - - while (1) // Read sequence of labels - { - const mDNSu8 len = *ptr++; // Read length of this label - if (len == 0) return(ptr); // If length is zero, that means this name is complete - switch (len & 0xC0) - { - case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label - { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } - if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label - { debugf("skipDomainName: Malformed domain name (more than 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); - } - } - } +{ + mDNSu16 total = 0; + + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } + + while (1) // Read sequence of labels + { + const mDNSu8 len = *ptr++; // Read length of this label + if (len == 0) return(ptr); // If length is zero, that means this name is complete + switch (len & 0xC0) + { + case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label + { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } + if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label + { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); } + ptr += len; + total += 1 + len; + break; + + case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL); + case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL); + case 0xC0: return(ptr+1); + } + } +} // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary. mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, - domainname *const name) - { - const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers - mDNSu8 *np = name->c; // Name pointer - const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer - - if (ptr < (mDNSu8*)msg || ptr >= end) - { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } - - *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) - - while (1) // Read sequence of labels - { - const mDNSu8 len = *ptr++; // Read length of this label - if (len == 0) break; // If length is zero, that means this name is complete - switch (len & 0xC0) - { - int i; - mDNSu16 offset; - - case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label - { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } - if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label - { debugf("getDomainName: Malformed domain name (more than 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); - } + domainname *const name) +{ + const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers + mDNSu8 *np = name->c; // Name pointer + const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer + + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } + + *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) + + while (1) // Read sequence of labels + { + const mDNSu8 len = *ptr++; // Read length of this label + if (len == 0) break; // If length is zero, that means this name is complete + switch (len & 0xC0) + { + int i; + mDNSu16 offset; + + case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label + { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } + if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label + { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); } + *np++ = len; + for (i=0; i<len; i++) *np++ = *ptr++; + *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) + break; + + case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c); + return(mDNSNULL); + + case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL); + + case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++); + if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers + ptr = (mDNSu8 *)msg + offset; + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); } + if (*ptr & 0xC0) + { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); } + break; + } + } + + if (nextbyte) return(nextbyte); + else return(ptr); +} mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) - { - mDNSu16 pktrdlength; - - ptr = skipDomainName(msg, ptr, end); - if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); } - - if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } - pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); - ptr += 10; - if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } - - return(ptr + pktrdlength); - } - -mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, - const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *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); - } +{ + 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); +} + +// Sanity check whether the NSEC/NSEC3 bitmap is good +mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int len) +{ + int win, wlen; + + while (bmap < end) + { + if (len < 3) + { + LogInfo("SanityCheckBitMap: invalid length %d", len); + return mDNSNULL; + } + + win = *bmap++; + wlen = *bmap++; + len -= 2; + if (len < wlen || wlen < 1 || wlen > 32) + { + LogInfo("SanityCheckBitMap: invalid window length %d", wlen); + return mDNSNULL; + } + if (win < 0 || win >= 256) + { + LogInfo("SanityCheckBitMap: invalid window %d", win); + return mDNSNULL; + } + + bmap += wlen; + len -= wlen; + } + return (mDNSu8 *)bmap; +} + +// This function is called with "msg" when we receive a DNS message and needs to parse a single resource record +// pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded +// (domainnames are expanded to 255 bytes) when stored in memory. +// +// This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr. +// The caller can do this only if the names in the resource records are compressed and validity of the +// resource record has already been done before. DNSSEC currently uses it this way. +mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, + LargeCacheRecord *const largecr, mDNSu16 rdlength) +{ + CacheRecord *const rr = &largecr->r; + RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; + + switch (rr->resrec.rrtype) + { + case kDNSType_A: + if (rdlength != sizeof(mDNSv4Addr)) + goto fail; + rdb->ipv4.b[0] = ptr[0]; + rdb->ipv4.b[1] = ptr[1]; + rdb->ipv4.b[2] = ptr[2]; + rdb->ipv4.b[3] = ptr[3]; + break; + + case kDNSType_NS: + case kDNSType_MD: + case kDNSType_MF: + case kDNSType_CNAME: + case kDNSType_MB: + case kDNSType_MG: + case kDNSType_MR: + case kDNSType_PTR: + case kDNSType_NSAP_PTR: + case kDNSType_DNAME: + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->name); + } + else + { + AssignDomainName(&rdb->name, (domainname *)ptr); + ptr += DomainNameLength(&rdb->name); + } + if (ptr != end) + { + debugf("SetRData: Malformed CNAME/PTR RDATA name"); + goto fail; + } + break; + + case kDNSType_SOA: + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->soa.mname); + } + else + { + AssignDomainName(&rdb->soa.mname, (domainname *)ptr); + ptr += DomainNameLength(&rdb->soa.mname); + } + if (!ptr) + { + debugf("SetRData: Malformed SOA RDATA mname"); + goto fail; + } + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->soa.rname); + } + else + { + AssignDomainName(&rdb->soa.rname, (domainname *)ptr); + ptr += DomainNameLength(&rdb->soa.rname); + } + if (!ptr) + { + debugf("SetRData: Malformed SOA RDATA rname"); + goto fail; + } + if (ptr + 0x14 != end) + { + debugf("SetRData: Malformed SOA RDATA"); + goto fail; + } + rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); + rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); + rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); + rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); + rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); + break; + + case kDNSType_NULL: + case kDNSType_HINFO: + case kDNSType_TXT: + case kDNSType_X25: + case kDNSType_ISDN: + case kDNSType_LOC: + case kDNSType_DHCID: + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + + case kDNSType_MX: + case kDNSType_AFSDB: + case kDNSType_RT: + case kDNSType_KX: + // Preference + domainname + if (rdlength < 3) + goto fail; + rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + ptr += 2; + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->mx.exchange); + } + else + { + AssignDomainName(&rdb->mx.exchange, (domainname *)ptr); + ptr += DomainNameLength(&rdb->mx.exchange); + } + if (ptr != end) + { + debugf("SetRData: Malformed MX name"); + goto fail; + } + break; + + case kDNSType_MINFO: + case kDNSType_RP: + // Domainname + domainname + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox); + } + else + { + AssignDomainName(&rdb->rp.mbox, (domainname *)ptr); + ptr += DomainNameLength(&rdb->rp.mbox); + } + if (!ptr) + { + debugf("SetRData: Malformed RP mbox"); + goto fail; + } + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->rp.txt); + } + else + { + AssignDomainName(&rdb->rp.txt, (domainname *)ptr); + ptr += DomainNameLength(&rdb->rp.txt); + } + if (ptr != end) + { + debugf("SetRData: Malformed RP txt"); + goto fail; + } + break; + + case kDNSType_PX: + // Preference + domainname + domainname + if (rdlength < 4) + goto fail; + rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + ptr += 2; + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->px.map822); + } + else + { + AssignDomainName(&rdb->px.map822, (domainname *)ptr); + ptr += DomainNameLength(&rdb->px.map822); + } + if (!ptr) + { + debugf("SetRData: Malformed PX map822"); + goto fail; + } + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400); + } + else + { + AssignDomainName(&rdb->px.mapx400, (domainname *)ptr); + ptr += DomainNameLength(&rdb->px.mapx400); + } + if (ptr != end) + { + debugf("SetRData: Malformed PX mapx400"); + goto fail; + } + break; + + case kDNSType_AAAA: + if (rdlength != sizeof(mDNSv6Addr)) + goto fail; + mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6)); + break; + + case kDNSType_SRV: + // Priority + weight + port + domainname + if (rdlength < 7) + goto fail; + rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + rdb->srv.port.b[0] = ptr[4]; + rdb->srv.port.b[1] = ptr[5]; + ptr += 6; + if (msg) + { + ptr = getDomainName(msg, ptr, end, &rdb->srv.target); + } + else + { + AssignDomainName(&rdb->srv.target, (domainname *)ptr); + ptr += DomainNameLength(&rdb->srv.target); + } + if (ptr != end) + { + debugf("SetRData: Malformed SRV RDATA name"); + goto fail; + } + break; + + case kDNSType_NAPTR: + { + int savelen, len; + domainname name; + const mDNSu8 *orig = ptr; + + // Make sure the data is parseable and within the limits. DNSSEC code looks at + // the domain name in the end for a valid domainname. + // + // Fixed length: Order, preference (4 bytes) + // Variable length: flags, service, regexp, domainname + + if (rdlength < 8) + goto fail; + // Order, preference. + ptr += 4; + // Parse flags, Service and Regexp + // length in the first byte does not include the length byte itself + len = *ptr + 1; + ptr += len; + if (ptr >= end) + { + LogInfo("SetRData: Malformed NAPTR flags"); + goto fail; + } + + // Service + len = *ptr + 1; + ptr += len; + if (ptr >= end) + { + LogInfo("SetRData: Malformed NAPTR service"); + goto fail; + } + + // Regexp + len = *ptr + 1; + ptr += len; + if (ptr >= end) + { + LogInfo("SetRData: Malformed NAPTR regexp"); + goto fail; + } + + savelen = ptr - orig; + + // RFC 2915 states that name compression is not allowed for this field. But RFC 3597 + // states that for NAPTR we should decompress. We make sure that we store the full + // name rather than the compressed name + if (msg) + { + ptr = getDomainName(msg, ptr, end, &name); + } + else + { + AssignDomainName(&name, (domainname *)ptr); + ptr += DomainNameLength(&name); + } + if (ptr != end) + { + LogInfo("SetRData: Malformed NAPTR RDATA name"); + goto fail; + } + + rr->resrec.rdlength = savelen + DomainNameLength(&name); + // The uncompressed size should not exceed the limits + if (rr->resrec.rdlength > MaximumRDSize) + { + LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->resrec.rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + goto fail; + } + mDNSPlatformMemCopy(rdb->data, orig, savelen); + AssignDomainName((domainname *)(rdb->data + savelen), &name); + break; + } + case kDNSType_OPT: { + mDNSu8 *dataend = rr->resrec.rdata->u.data; + rdataOPT *opt = rr->resrec.rdata->u.opt; + rr->resrec.rdlength = 0; + while (ptr < end && (mDNSu8 *)(opt+1) < &dataend[MaximumRDSize]) + { + const rdataOPT *const currentopt = opt; + if (ptr + 4 > end) { LogInfo("SetRData: OPT RDATA ptr + 4 > end"); goto fail; } + opt->opt = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + opt->optlen = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + ptr += 4; + if (ptr + opt->optlen > end) { LogInfo("SetRData: ptr + opt->optlen > end"); goto fail; } + switch (opt->opt) + { + case kDNSOpt_LLQ: + if (opt->optlen == DNSOpt_LLQData_Space - 4) + { + opt->u.llq.vers = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + opt->u.llq.err = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8); + opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]); + if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond) + opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond; + opt++; + } + break; + case kDNSOpt_Lease: + if (opt->optlen == DNSOpt_LeaseData_Space - 4) + { + opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); + if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond) + opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond; + opt++; + } + break; + case kDNSOpt_Owner: + if (ValidOwnerLength(opt->optlen)) + { + opt->u.owner.vers = ptr[0]; + opt->u.owner.seq = ptr[1]; + mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address + mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address + opt->u.owner.password = zeroEthAddr; + if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) + { + mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address + // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above + // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 + if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) + mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4)); + } + opt++; + } + break; + case kDNSOpt_Trace: + if (opt->optlen == DNSOpt_TraceData_Space - 4) + { + opt->u.tracer.platf = ptr[0]; + opt->u.tracer.mDNSv = (mDNSu32) ((mDNSu32)ptr[1] << 24 | (mDNSu32)ptr[2] << 16 | (mDNSu32)ptr[3] << 8 | ptr[4]); + opt++; + } + else + { + opt->u.tracer.platf = 0xFF; + opt->u.tracer.mDNSv = 0xFFFFFFFF; + opt++; + } + break; + } + ptr += currentopt->optlen; + } + rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data); + if (ptr != end) { LogInfo("SetRData: Malformed OptRdata"); goto fail; } + break; + } + + case kDNSType_NSEC: { + domainname name; + int len = rdlength; + int bmaplen, dlen; + const mDNSu8 *orig = ptr; + const mDNSu8 *bmap; + + if (msg) + { + ptr = getDomainName(msg, ptr, end, &name); + } + else + { + AssignDomainName(&name, (domainname *)ptr); + ptr += DomainNameLength(&name); + } + if (!ptr) + { + LogInfo("SetRData: Malformed NSEC nextname"); + goto fail; + } + + dlen = DomainNameLength(&name); + + // Multicast NSECs use name compression for this field unlike the unicast case which + // does not use compression. And multicast case always succeeds in compression. So, + // the rdlength includes only the compressed space in that case. So, can't + // use the DomainNameLength of name to reduce the length here. + len -= (ptr - orig); + bmaplen = len; // Save the length of the bitmap + bmap = ptr; + ptr = SanityCheckBitMap(bmap, end, len); + if (!ptr) + goto fail; + if (ptr != end) + { + LogInfo("SetRData: Malformed NSEC length not right"); + goto fail; + } + + // Initialize the right length here. When we call SetNewRData below which in turn calls + // GetRDLength and for NSEC case, it assumes that rdlength is intitialized + rr->resrec.rdlength = DomainNameLength(&name) + bmaplen; + + // Do we have space after the name expansion ? + if (rr->resrec.rdlength > MaximumRDSize) + { + LogInfo("SetRData: Malformed NSEC rdlength %d, rr->resrec.rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + goto fail; + } + AssignDomainName((domainname *)rdb->data, &name); + mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen); + break; + } + case kDNSType_NSEC3: + { + rdataNSEC3 *nsec3 = (rdataNSEC3 *)ptr; + mDNSu8 *p = (mDNSu8 *)&nsec3->salt; + int hashLength, bitmaplen; + + if (rdlength < NSEC3_FIXED_SIZE + 1) + { + LogInfo("SetRData: NSEC3 too small length %d", rdlength); + goto fail; + } + if (nsec3->alg != SHA1_DIGEST_TYPE) + { + LogInfo("SetRData: nsec3 alg %d not supported", nsec3->alg); + goto fail; + } + if (swap16(nsec3->iterations) > NSEC3_MAX_ITERATIONS) + { + LogInfo("SetRData: nsec3 iteration count %d too big", swap16(nsec3->iterations)); + goto fail; + } + p += nsec3->saltLength; + // There should at least be one byte beyond saltLength + if (p >= end) + { + LogInfo("SetRData: nsec3 too small, at saltlength %d, p %p, end %p", nsec3->saltLength, p, end); + goto fail; + } + // p is pointing at hashLength + hashLength = (int)*p++; + if (!hashLength) + { + LogInfo("SetRData: hashLength zero"); + goto fail; + } + p += hashLength; + if (p > end) + { + LogInfo("SetRData: nsec3 too small, at hashLength %d, p %p, end %p", hashLength, p, end); + goto fail; + } + + bitmaplen = rdlength - (int)(p - ptr); + p = SanityCheckBitMap(p, end, bitmaplen); + if (!p) + goto fail; + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + case kDNSType_TKEY: + case kDNSType_TSIG: + { + domainname name; + int dlen, rlen; + + // The name should not be compressed. But we take the conservative approach + // and uncompress the name before we store it. + if (msg) + { + ptr = getDomainName(msg, ptr, end, &name); + } + else + { + AssignDomainName(&name, (domainname *)ptr); + ptr += DomainNameLength(&name); + } + if (!ptr) + { + LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->resrec.rrtype); + goto fail; + } + dlen = DomainNameLength(&name); + rlen = end - ptr; + rr->resrec.rdlength = dlen + rlen; + AssignDomainName((domainname *)rdb->data, &name); + mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen); + break; + } + case kDNSType_RRSIG: + { + const mDNSu8 *sig = ptr + RRSIG_FIXED_SIZE; + const mDNSu8 *orig = sig; + domainname name; + if (rdlength < RRSIG_FIXED_SIZE + 1) + { + LogInfo("SetRData: RRSIG too small length %d", rdlength); + goto fail; + } + if (msg) + { + sig = getDomainName(msg, sig, end, &name); + } + else + { + AssignDomainName(&name, (domainname *)sig); + sig += DomainNameLength(&name); + } + if (!sig) + { + LogInfo("SetRData: Malformed RRSIG record"); + goto fail; + } + + if ((sig - orig) != DomainNameLength(&name)) + { + LogInfo("SetRData: Malformed RRSIG record, signer name compression"); + goto fail; + } + // Just ensure that we have at least one byte of the signature + if (sig + 1 >= end) + { + LogInfo("SetRData: Not enough bytes for signature type %d", rr->resrec.rrtype); + goto fail; + } + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + case kDNSType_DNSKEY: + { + if (rdlength < DNSKEY_FIXED_SIZE + 1) + { + LogInfo("SetRData: DNSKEY too small length %d", rdlength); + goto fail; + } + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + case kDNSType_DS: + { + if (rdlength < DS_FIXED_SIZE + 1) + { + LogInfo("SetRData: DS too small length %d", rdlength); + goto fail; + } + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + default: + debugf("SetRData: 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 = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } + return mDNStrue; +fail: + return mDNSfalse; +} + +mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, + const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr) +{ + CacheRecord *const rr = &largecr->r; + mDNSu16 pktrdlength; + + if (largecr == &m->rec && m->rec.r.resrec.RecordType) + { + LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); +#if ForceAlerts + *(long*)0 = 0; +#endif + } + + rr->next = mDNSNULL; + rr->resrec.name = &largecr->namestorage; + + rr->NextInKAList = mDNSNULL; + rr->TimeRcvd = m ? m->timenow : 0; + rr->DelayDelivery = 0; + rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord() + rr->LastUsed = m ? m->timenow : 0; + rr->CRActiveQuestion = mDNSNULL; + rr->UnansweredQueries = 0; + rr->LastUnansweredTime= 0; +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + rr->MPUnansweredQ = 0; + rr->MPLastUnansweredQT= 0; + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; +#endif + rr->NextInCFList = mDNSNULL; + + rr->resrec.InterfaceID = InterfaceID; + rr->resrec.rDNSServer = mDNSNULL; + + ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL + if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); } + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + + if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } + + rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); + rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask); + rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]); + if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1) + rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond; + // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for + // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly. + pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); + + // If mDNS record has cache-flush bit set, we mark it unique + // For uDNS records, all are implicitly deemed unique (a single DNS server is always + // authoritative for the entire RRSet), unless this is a truncated response + if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC))) + RecordType |= kDNSRecordTypePacketUniqueMask; + ptr += 10; + if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record + + rr->resrec.rdata = (RData*)&rr->smallrdatastorage; + rr->resrec.rdata->MaxRDLength = MaximumRDSize; + + if (pktrdlength > MaximumRDSize) + { + LogInfo("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)", + DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); + goto fail; + } + + if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c); + + // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding + // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind + // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data. + // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that + // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ. + if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136) + rr->resrec.rdlength = 0; + else if (!SetRData(msg, ptr, end, largecr, pktrdlength)) + goto fail; + + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us + + // Success! Now fill in RecordType to show this record contains valid data + rr->resrec.RecordType = RecordType; + return(end); + +fail: + // If we were unable to parse the rdata in this record, we indicate that by + // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero + rr->resrec.RecordType = kDNSRecordTypePacketNegative; + rr->resrec.rdlength = 0; + rr->resrec.rdestimate = 0; + rr->resrec.rdatahash = 0; + return(end); +} mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) - { - ptr = skipDomainName(msg, ptr, end); - if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); } - if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } - return(ptr+4); - } +{ + 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); - } + DNSQuestion *question) +{ + mDNSPlatformMemZero(question, sizeof(*question)); + question->InterfaceID = InterfaceID; + if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast + ptr = getDomainName(msg, ptr, end, &question->qname); + if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); } + if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } + + question->qnamehash = DomainNameHashValue(&question->qname); + question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type + question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class + return(ptr+4); +} mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end) - { - int i; - const mDNSu8 *ptr = msg->data; - for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end); - return(ptr); - } +{ + 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); - } +{ + 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); - } +{ + int i; + const mDNSu8 *ptr = LocateAuthorities(msg, end); + for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end); + return (ptr); +} + +mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize) +{ + int i; + const mDNSu8 *ptr = LocateAdditionals(msg, end); + + // Locate the OPT record. + // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response." + // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section, + // but not necessarily the *last* entry in the Additional Section. + for (i = 0; ptr && i < msg->h.numAdditionals; i++) + { + if (ptr + DNSOpt_Header_Space + minsize <= end && // Make sure we have 11+minsize bytes of data + ptr[0] == 0 && // Name must be root label + ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT + ptr[2] == (kDNSType_OPT & 0xFF) && + ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize) + return(ptr); + else + ptr = skipResourceRecord(msg, ptr, end); + } + return(mDNSNULL); +} + +// On success, GetLLQOptData returns pointer to storage within shared "m->rec"; +// it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use +// Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together +// The code that currently calls this assumes there's only one, instead of iterating through the set +mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end) +{ + const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space); + if (ptr) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]); + } + return(mDNSNULL); +} + +// Get the lease life of records in a dynamic update +// returns 0 on error or if no lease present +mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end) +{ + mDNSu32 result = 0; + const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space); + if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.rdlength >= DNSOpt_LeaseData_Space && m->rec.r.resrec.rdata->u.opt[0].opt == kDNSOpt_Lease) + result = m->rec.r.resrec.rdata->u.opt[0].u.updatelease; + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + return(result); +} + +mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label) +{ + int i; + LogMsg("%2d %s", count, label); + for (i = 0; i < count && ptr; i++) + { + // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage, + // but since it's only used for debugging (and probably only on OS X, not on + // embedded systems) putting a 9kB object on the stack isn't a big problem. + LargeCacheRecord largecr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr); + if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); + } + if (!ptr) LogMsg("DumpRecords: ERROR: Premature end of packet data"); + return(ptr); +} + +#define DNS_OP_Name(X) ( \ + (X) == kDNSFlag0_OP_StdQuery ? "" : \ + (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \ + (X) == kDNSFlag0_OP_Status ? "Status " : \ + (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \ + (X) == kDNSFlag0_OP_Notify ? "Notify " : \ + (X) == kDNSFlag0_OP_Update ? "Update " : "?? " ) + +#define DNS_RC_Name(X) ( \ + (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \ + (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \ + (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \ + (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \ + (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \ + (X) == kDNSFlag1_RC_Refused ? "Refused" : \ + (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \ + (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \ + (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \ + (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \ + (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" ) + +// Note: DumpPacket expects the packet header fields in host byte order, not network byte order +mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport, + const mDNSAddr *srcaddr, mDNSIPPort srcport, + const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) +{ + mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update); + const mDNSu8 *ptr = msg->data; + int i; + DNSQuestion q; + char tbuffer[64], sbuffer[64], dbuffer[64] = ""; + if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0; + else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receive")] = 0; + if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0; + else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0; + if (dstaddr || !mDNSIPPortIsZero(dstport)) + dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0; + + LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --", + tbuffer, transport, + DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), + msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query", + msg->h.flags.b[0], msg->h.flags.b[1], + DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask), + msg->h.flags.b[1] & kDNSFlag1_RC_Mask, + msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "", + msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "", + msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "", + msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "", + msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "", + msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "", + mDNSVal16(msg->h.id), + end - msg->data, + sbuffer, mDNSVal16(srcport), dbuffer, + (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : "" + ); + + LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); + for (i = 0; i < msg->h.numQuestions && ptr; i++) + { + ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q); + if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); + } + ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers"); + ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities"); + ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals"); + LogMsg("--------------"); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - -#pragma mark - #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; - } +// Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.) +struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; + +struct UDPSocket_struct +{ + mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port +}; + +// Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which +// is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible. +mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end, + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo, + mDNSBool useBackgroundTrafficClass) +{ + mStatus status = mStatus_NoError; + const mDNSu16 numAdditionals = msg->h.numAdditionals; + mDNSu8 *newend; + mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; + +#if APPLE_OSX_mDNSResponder + // maintain outbound packet statistics + if (mDNSOpaque16IsZero(msg->h.id)) + m->MulticastPacketsSent++; + else + m->UnicastPacketsSent++; +#endif // APPLE_OSX_mDNSResponder + + // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code + if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData) + { + LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data); + return mStatus_BadParamErr; + } + + newend = putHINFO(m, msg, end, authInfo, limit); + if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal + else end = newend; + + // Put all the integer values in IETF byte-order (MSB first, LSB second) + SwapDNSHeaderBytes(msg); + + if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order + if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; } + else + { + // Send the packet on the wire + if (!sock) + status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport, useBackgroundTrafficClass); + else + { + mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg); + mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) }; + char *buf; + long nsent; + + // Try to send them in one packet if we can allocate enough memory + buf = mDNSPlatformMemAllocate(msglen + 2); + if (buf) + { + buf[0] = lenbuf[0]; + buf[1] = lenbuf[1]; + mDNSPlatformMemCopy(buf+2, msg, msglen); + nsent = mDNSPlatformWriteTCP(sock, buf, msglen+2); + if (nsent != (msglen + 2)) + { + LogMsg("mDNSSendDNSMessage: write message failed %d/%d", nsent, msglen); + status = mStatus_ConnFailed; + } + mDNSPlatformMemFree(buf); + } + else + { + nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); + if (nsent != 2) + { + LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); + status = mStatus_ConnFailed; + } + else + { + nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen); + if (nsent != msglen) + { + LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); + status = mStatus_ConnFailed; + } + } + } + } + } + + // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage) + SwapDNSHeaderBytes(msg); + + // Dump the packet with the HINFO and TSIG + if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id)) { + mDNSIPPort port = MulticastDNSPort; + DumpPacket(m, status, mDNStrue, sock && + (sock->flags & kTCPSocketFlags_UseTLS) ? + "TLS" : sock ? "TCP" : "UDP", mDNSNULL, + src ? src->port : port, dst, dstport, msg, end); + } + + // put the number of additionals back the way it was + msg->h.numAdditionals = numAdditionals; + + return(status); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2097,85 +3839,497 @@ mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg #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; - LogMsgNoIdent("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++; - } +mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname) +{ + // MUST grab the platform lock FIRST! + mDNSPlatformLock(m); + + // Normally, mDNS_reentrancy is zero and so is mDNS_busy + // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too + // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one + // If mDNS_busy != mDNS_reentrancy that's a bad sign + if (m->mDNS_busy != m->mDNS_reentrancy) + { + LogMsg("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); +#if ForceAlerts + *(long*)0 = 0; +#endif + } + + // If this is an initial entry into the mDNSCore code, set m->timenow + // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set + if (m->mDNS_busy == 0) + { + if (m->timenow) + LogMsg("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m)); + m->timenow = mDNS_TimeNow_NoLock(m); + if (m->timenow == 0) m->timenow = 1; + } + else if (m->timenow == 0) + { + LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy); + m->timenow = mDNS_TimeNow_NoLock(m); + if (m->timenow == 0) m->timenow = 1; + } + + if (m->timenow_last - m->timenow > 0) + { + m->timenow_adjust += m->timenow_last - m->timenow; + LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust); + m->timenow = m->timenow_last; + } + m->timenow_last = m->timenow; + + // Increment mDNS_busy so we'll recognise re-entrant calls + m->mDNS_busy++; +} + +mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m) +{ + AuthRecord *rr; + for (rr = m->NewLocalRecords; rr; rr = rr->next) + if (LocalRecordReady(rr)) return rr; + return mDNSNULL; +} mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) - { - mDNSs32 e = m->timenow + 0x78000000; - if (m->mDNSPlatformStatus != mStatus_NoError || 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); +{ + mDNSs32 e = m->timenow + 0x78000000; + if (m->mDNSPlatformStatus != mStatus_NoError) return(e); + if (m->NewQuestions) + { + if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering; + else return(m->timenow); + } + if (m->NewLocalOnlyQuestions) return(m->timenow); + if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow); + if (m->NewLocalOnlyRecords) return(m->timenow); + if (m->SPSProxyListChanged) return(m->timenow); + if (m->LocalRemoveEvents) return(m->timenow); + +#ifndef UNICAST_DISABLED + if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent; + if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp; + if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate; +#endif + + if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; + if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; + if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA; + + // NextScheduledSPRetry only valid when DelaySleep not set + if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; + if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep; + + if (m->SuppressSending) + { + if (e - m->SuppressSending > 0) e = m->SuppressSending; + } + else + { + if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; + if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; + if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; + } + if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime; + return(e); +} + +mDNSexport void ShowTaskSchedulingError(mDNS *const m) +{ + AuthRecord *rr; + mDNS_Lock(m); + + LogMsg("Task Scheduling Error: Continuously busy for more than a second"); + + // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above + + if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0)) + LogMsg("Task Scheduling Error: NewQuestion %##s (%s)", + m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); + + if (m->NewLocalOnlyQuestions) + LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)", + m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); + + if (m->NewLocalRecords) + { + rr = AnyLocalRecordReady(m); + if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr)); + } + + if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords"); + + if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged"); + if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents"); + + if (m->timenow - m->NextScheduledEvent >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent); + #ifndef UNICAST_DISABLED - if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent; + if (m->timenow - m->NextuDNSEvent >= 0) + LogMsg("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent); + if (m->timenow - m->NextScheduledNATOp >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp); + if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) + LogMsg("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate); #endif - if (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); - } + + if (m->timenow - m->NextCacheCheck >= 0) + LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck); + if (m->timenow - m->NextScheduledSPS >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS); + if (m->timenow - m->NextScheduledKA >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledKA %d", m->timenow - m->NextScheduledKA); + if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry); + if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) + LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep); + + if (m->SuppressSending && m->timenow - m->SuppressSending >= 0) + LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending); + if (m->timenow - m->NextScheduledQuery >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery); + if (m->timenow - m->NextScheduledProbe >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe); + if (m->timenow - m->NextScheduledResponse >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse); + + mDNS_Unlock(m); +} + +mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname) +{ + // Decrement mDNS_busy + m->mDNS_busy--; + + // Check for locking failures + if (m->mDNS_busy != m->mDNS_reentrancy) + { + LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); +#if ForceAlerts + *(long*)0 = 0; +#endif + } + + // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow + if (m->mDNS_busy == 0) + { + m->NextScheduledEvent = GetNextScheduledEvent(m); + if (m->timenow == 0) LogMsg("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname); + m->timenow = 0; + } + + // MUST release the platform lock LAST! + mDNSPlatformUnlock(m); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Specialized mDNS version of vsnprintf +#endif + +static const struct mDNSprintf_format +{ + unsigned leftJustify : 1; + unsigned forceSign : 1; + unsigned zeroPad : 1; + unsigned havePrecision : 1; + unsigned hSize : 1; + unsigned lSize : 1; + char altForm; + char sign; // +, - or space + unsigned int fieldWidth; + unsigned int precision; +} mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) +{ + mDNSu32 nwritten = 0; + int c; + if (buflen == 0) return(0); + buflen--; // Pre-reserve one space in the buffer for the terminating null + if (buflen == 0) goto exit; + + for (c = *fmt; c != 0; c = *++fmt) + { + if (c != '%') + { + *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + } + else + { + unsigned int i=0, j; + // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for + // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. + // The size needs to be enough for a 256-byte domain name plus some error text. + #define mDNS_VACB_Size 300 + char mDNS_VACB[mDNS_VACB_Size]; + #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) + #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s)) + char *s = mDNS_VACB_Lim, *digits; + struct mDNSprintf_format F = mDNSprintf_format_default; + + while (1) // decode flags + { + c = *++fmt; + if (c == '-') F.leftJustify = 1; + else if (c == '+') F.forceSign = 1; + else if (c == ' ') F.sign = ' '; + else if (c == '#') F.altForm++; + else if (c == '0') F.zeroPad = 1; + else break; + } + + if (c == '*') // decode field width + { + int f = va_arg(arg, int); + if (f < 0) { f = -f; F.leftJustify = 1; } + F.fieldWidth = (unsigned int)f; + c = *++fmt; + } + else + { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); + } + + if (c == '.') // decode precision + { + if ((c = *++fmt) == '*') + { F.precision = va_arg(arg, unsigned int); c = *++fmt; } + else for (; c >= '0' && c <= '9'; c = *++fmt) + F.precision = (10 * F.precision) + (c - '0'); + F.havePrecision = 1; + } + + if (F.leftJustify) F.zeroPad = 0; + +conv: + switch (c) // perform appropriate conversion + { + unsigned long n; + case 'h': F.hSize = 1; c = *++fmt; goto conv; + case 'l': // fall through + case 'L': F.lSize = 1; c = *++fmt; goto conv; + case 'd': + case 'i': if (F.lSize) n = (unsigned long)va_arg(arg, long); + else n = (unsigned long)va_arg(arg, int); + if (F.hSize) n = (short) n; + if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } + else if (F.forceSign) F.sign = '+'; + goto decimal; + case 'u': if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + F.sign = 0; + goto decimal; +decimal: if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.sign) --F.precision; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); + for (; i < F.precision; i++) *--s = '0'; + if (F.sign) { *--s = F.sign; i++; } + break; + + case 'o': if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + if (!F.havePrecision) + { + if (F.zeroPad) F.precision = F.fieldWidth; + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); + if (F.altForm && i && *s != '0') { *--s = '0'; i++; } + for (; i < F.precision; i++) *--s = '0'; + break; + + case 'a': { + unsigned char *a = va_arg(arg, unsigned char *); + if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } + else + { + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (F.altForm) + { + mDNSAddr *ip = (mDNSAddr*)a; + switch (ip->type) + { + case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; + case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; + default: F.precision = 0; break; + } + } + if (F.altForm && !F.precision) + i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»"); + else switch (F.precision) + { + case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d", + a[0], a[1], a[2], a[3]); break; + case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", + a[0], a[1], a[2], a[3], a[4], a[5]); break; + case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), + "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], + a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; + default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify" + " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; + } + } + } + break; + + case 'p': F.havePrecision = F.lSize = 1; + F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit + case 'X': digits = "0123456789ABCDEF"; + goto hexadecimal; + case 'x': digits = "0123456789abcdef"; +hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.altForm) F.precision -= 2; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; + for (; i < F.precision; i++) *--s = '0'; + if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } + break; + + case 'c': *--s = (char)va_arg(arg, int); i = 1; break; + + case 's': s = va_arg(arg, char *); + if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } + else switch (F.altForm) + { + case 0: i=0; + if (!F.havePrecision) // C string + while (s[i]) i++; + else + { + while ((i < F.precision) && s[i]) i++; + // Make sure we don't truncate in the middle of a UTF-8 character + // If last character we got was any kind of UTF-8 multi-byte character, + // then see if we have to back up. + // This is not as easy as the similar checks below, because + // here we can't assume it's safe to examine the *next* byte, so we + // have to confine ourselves to working only backwards in the string. + j = i; // Record where we got to + // Now, back up until we find first non-continuation-char + while (i>0 && (s[i-1] & 0xC0) == 0x80) i--; + // Now s[i-1] is the first non-continuation-char + // and (j-i) is the number of continuation-chars we found + if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char + { + i--; // Tentatively eliminate this start-char as well + // Now (j-i) is the number of characters we're considering eliminating. + // To be legal UTF-8, the start-char must contain (j-i) one-bits, + // followed by a zero bit. If we shift it right by (7-(j-i)) bits + // (with sign extension) then the result has to be 0xFE. + // If this is right, then we reinstate the tentatively eliminated bytes. + if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j; + } + } + break; + case 1: i = (unsigned char) *s++; break; // Pascal string + case 2: { // DNS label-sequence name + unsigned char *a = (unsigned char *)s; + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (*a == 0) *s++ = '.'; // Special case for root DNS name + while (*a) + { + char buf[63*4+1]; + if (*a > 63) + { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; } + if (s + *a >= &mDNS_VACB[254]) + { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; } + // Need to use ConvertDomainLabelToCString to do proper escaping here, + // so it's clear what's a literal dot and what's a label separator + ConvertDomainLabelToCString((domainlabel*)a, buf); + s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf); + a += 1 + *a; + } + i = (mDNSu32)(s - mDNS_VACB); + s = mDNS_VACB; // Reset s back to the start of the buffer + break; + } + } + // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below) + if (F.havePrecision && i > F.precision) + { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + break; + + case 'n': s = va_arg(arg, char *); + if (F.hSize) *(short *) s = (short)nwritten; + else if (F.lSize) *(long *) s = (long)nwritten; + else *(int *) s = (int)nwritten; + continue; + + default: s = mDNS_VACB; + i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c); + + case '%': *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + break; + } + + if (i < F.fieldWidth && !F.leftJustify) // Pad on the left + do { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } while (i < --F.fieldWidth); + + // Make sure we don't truncate in the middle of a UTF-8 character. + // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the + // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, + // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly + // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). + if (i > buflen - nwritten) + { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result + nwritten += i; + if (nwritten >= buflen) goto exit; + + for (; i < F.fieldWidth; i++) // Pad on the right + { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } + } + } +exit: + *sbuffer++ = 0; + return(nwritten); +} + +mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) +{ + mDNSu32 length; + + va_list ptr; + va_start(ptr,fmt); + length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr); + va_end(ptr); + + return(length); +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h index 717d87702a..b92f5a9ab4 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h @@ -5,148 +5,33 @@ * 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" { +#ifdef __cplusplus +extern "C" { #endif +//************************************************************************************************************* +// Macros + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -154,51 +39,50 @@ Revision 1.1 2003/12/13 03:05:27 ksekar #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; +{ + kDNSFlag0_QR_Mask = 0x80, // Query or response? + kDNSFlag0_QR_Query = 0x00, + kDNSFlag0_QR_Response = 0x80, + + kDNSFlag0_OP_Mask = 0x78, // Operation type + kDNSFlag0_OP_StdQuery = 0x00, + kDNSFlag0_OP_Iquery = 0x08, + kDNSFlag0_OP_Status = 0x10, + kDNSFlag0_OP_Unused3 = 0x18, + kDNSFlag0_OP_Notify = 0x20, + kDNSFlag0_OP_Update = 0x28, + + kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, + + kDNSFlag0_AA = 0x04, // Authoritative Answer? + kDNSFlag0_TC = 0x02, // Truncated? + kDNSFlag0_RD = 0x01, // Recursion Desired? + kDNSFlag1_RA = 0x80, // Recursion Available? + + kDNSFlag1_Zero = 0x40, // Reserved; must be zero + kDNSFlag1_AD = 0x20, // Authentic Data [RFC 2535] + kDNSFlag1_CD = 0x10, // Checking Disabled [RFC 2535] + + kDNSFlag1_RC_Mask = 0x0F, // Response code + kDNSFlag1_RC_NoErr = 0x00, + kDNSFlag1_RC_FormErr = 0x01, + kDNSFlag1_RC_ServFail = 0x02, + kDNSFlag1_RC_NXDomain = 0x03, + kDNSFlag1_RC_NotImpl = 0x04, + kDNSFlag1_RC_Refused = 0x05, + kDNSFlag1_RC_YXDomain = 0x06, + kDNSFlag1_RC_YXRRSet = 0x07, + kDNSFlag1_RC_NXRRSet = 0x08, + kDNSFlag1_RC_NotAuth = 0x09, + kDNSFlag1_RC_NotZone = 0x0A +} DNS_Flags; typedef enum - { - TSIG_ErrBadSig = 16, - TSIG_ErrBadKey = 17, - TSIG_ErrBadTime = 18 - } TSIG_ErrorCode; - +{ + TSIG_ErrBadSig = 16, + TSIG_ErrBadKey = 17, + TSIG_ErrBadTime = 18 +} TSIG_ErrorCode; // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -206,107 +90,146 @@ typedef enum #pragma mark - General Utility Functions #endif -extern const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf); +extern NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf); extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf); -extern mDNSu32 mDNSRandom(mDNSu32 max); -extern mDNSu32 mDNSRandomFromFixedSeed(mDNSu32 seed, mDNSu32 max); - +extern mDNSu32 mDNSRandom(mDNSu32 max); // Returns pseudo-random result from zero to max inclusive // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Domain Name Utility Functions #endif - -#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') + +#define mDNSSubTypeLabel "\x04_sub" + +#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) == '-') ) +#define mDNSIsLetter(X) (mDNSIsUpperCase(X) || mDNSIsLowerCase(X)) + +#define mDNSValidHostChar(X, notfirst, notlast) (mDNSIsLetter(X) || mDNSIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') ) extern mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent); +extern int CountLabels(const domainname *d); +extern const domainname *SkipLeadingLabels(const domainname *d, int skip); extern mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max); extern mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText); extern mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText); -extern void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText); -extern void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); +extern void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText); #define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME) - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Resource Record Utility Functions #endif -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); - +// IdenticalResourceRecord returns true if two resources records have +// the same name, type, class, and identical rdata (InterfaceID and TTL may differ) + +// IdenticalSameNameRecord is the same, except it skips the expensive SameDomainName() check, +// which is at its most expensive and least useful in cases where we know in advance that the names match + +// Note: The dominant use of IdenticalResourceRecord is from ProcessQuery(), handling known-answer lists. In this case +// it's common to have a whole bunch or records with exactly the same name (e.g. "_http._tcp.local") but different RDATA. +// The SameDomainName() check is expensive when the names match, and in this case *all* the names match, so we +// used to waste a lot of CPU time verifying that the names match, only then to find that the RDATA is different. +// We observed mDNSResponder spending 30% of its total CPU time on this single task alone. +// By swapping the checks so that we check the RDATA first, we can quickly detect when it's different +// (99% of the time) and then bail out before we waste time on the expensive SameDomainName() check. + +#define IdenticalResourceRecord(r1,r2) ( \ + (r1)->rrtype == (r2)->rrtype && \ + (r1)->rrclass == (r2)->rrclass && \ + (r1)->namehash == (r2)->namehash && \ + (r1)->rdlength == (r2)->rdlength && \ + (r1)->rdatahash == (r2)->rdatahash && \ + SameRDataBody((r1), &(r2)->rdata->u, SameDomainName) && \ + SameDomainName((r1)->name, (r2)->name)) + +#define IdenticalSameNameRecord(r1,r2) ( \ + (r1)->rrtype == (r2)->rrtype && \ + (r1)->rrclass == (r2)->rrclass && \ + (r1)->rdlength == (r2)->rdlength && \ + (r1)->rdatahash == (r2)->rdatahash && \ + SameRDataBody((r1), &(r2)->rdata->u, SameDomainName)) + +// A given RRType answers a QuestionType if RRType is CNAME, or types match, or QuestionType is ANY, +// or the RRType is NSEC and positively asserts the nonexistence of the type being requested +#define RRTypeAnswersQuestionType(R,Q) ((R)->rrtype == kDNSType_CNAME || (R)->rrtype == (Q) || (Q) == kDNSQType_ANY || RRAssertsNonexistence((R),(Q))) +// Unicast NSEC records have the NSEC bit set whereas the multicast NSEC ones don't +#define UNICAST_NSEC(rr) ((rr)->rrtype == kDNSType_NSEC && RRAssertsExistence((rr), kDNSType_NSEC)) + +extern mDNSu32 RDataHashValue(const ResourceRecord *const rr); +extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename); +extern mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); - -extern mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2); - +extern mDNSBool AnyTypeRecordAnswersQuestion (const ResourceRecord *const rr, const DNSQuestion *const q); +extern mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q); +extern mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const rr, const DNSQuestion *const q); extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate); +extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd); +extern mStatus DNSNameToLowerCase(domainname *d, domainname *result); #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) + ((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name : \ + ((RR)->rrtype == kDNSType_MX || (RR)->rrtype == kDNSType_AFSDB || (RR)->rrtype == kDNSType_RT || (RR)->rrtype == kDNSType_KX ) ? &(RR)->rdata->u.mx.exchange : \ + ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL ) +#define LocalRecordReady(X) ((X)->resrec.RecordType != kDNSRecordTypeUnique) // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - -#pragma mark - #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); +extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr); // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes, -// but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet +// 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 AllowedRRSpace(msg) (((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) -#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) +extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit); -#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); +#define PutResourceRecordTTL(msg, ptr, count, rr, ttl) \ + PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AllowedRRSpace(msg)) -extern mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr); +#define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) \ + PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AbsoluteMaxDNSMessageData) -extern mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass); +#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) -extern mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass); +// The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg, +// and assume local variables 'OwnerRecordSpace' & 'TraceRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER/TRACER option at the end +#define PutRR_OS_TTL(ptr, count, rr, ttl) \ + PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace) -extern mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end); +#define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl) +extern mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass); +extern mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass); +extern mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end); extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr); - -extern mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype); - +extern mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit); +extern mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit); extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name); - extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); - -#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) +extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit); + +extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit); +extern mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit); +extern int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg); +extern void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap); +extern const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen, + const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -314,43 +237,47 @@ extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); #pragma mark - DNS Message Parsing Functions #endif +#define AuthHashSlot(X) (DomainNameHashValue(X) % AUTH_HASH_SLOTS) +#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS) extern mDNSu32 DomainNameHashValue(const domainname *const name); - extern void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength); - - extern const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end); - extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, - domainname *const name); - + 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 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 * end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr); +extern mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, + LargeCacheRecord *const largecr, mDNSu16 rdlength); 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); - + DNSQuestion *question); extern const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end); - extern const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end); - extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end); - +extern const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize); +extern const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end); +extern mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end); +extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport, + const mDNSAddr *srcaddr, mDNSIPPort srcport, + const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end); +extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type); +extern mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type); +extern mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type); + +extern mDNSu16 swap16(mDNSu16 x); +extern mDNSu32 swap32(mDNSu32 x); // *************************************************************************** #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); - +extern mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end, + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo, + mDNSBool useBackgroundTrafficClass); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -358,11 +285,31 @@ extern mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mD #pragma mark - RR List Management & Task Management #endif -extern void mDNS_Lock(mDNS *const m); -extern void mDNS_Unlock(mDNS *const m); +extern void ShowTaskSchedulingError(mDNS *const m); +extern void mDNS_Lock_(mDNS *const m, const char * const functionname); +extern void mDNS_Unlock_(mDNS *const m, const char * const functionname); + +#if defined(_WIN32) + #define __func__ __FUNCTION__ +#endif + +#define mDNS_Lock(X) mDNS_Lock_((X), __func__) + +#define mDNS_Unlock(X) mDNS_Unlock_((X), __func__) + +#define mDNS_CheckLock(X) { if ((X)->mDNS_busy != (X)->mDNS_reentrancy+1) \ + LogMsg("%s: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", __func__, (X)->mDNS_busy, (X)->mDNS_reentrancy); } + +#define mDNS_DropLockBeforeCallback() do { m->mDNS_reentrancy++; \ + if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Locking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ +} while (0) + +#define mDNS_ReclaimLockAfterCallback() do { \ + if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Unlocking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ + m->mDNS_reentrancy--; } while (0) -#ifdef __cplusplus - } +#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 index 6d0346c4f2..33798d3833 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c @@ -1,81 +1,20 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2011 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" { @@ -85,14 +24,30 @@ extern "C" { #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) +#if (defined(_MSC_VER)) +// Disable "conditional expression is constant" warning for debug macros. +// Otherwise, this generates warnings for the perfectly natural construct "while(1)" +// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know + #pragma warning(disable:4127) +#endif + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Byte Swapping Functions #endif - // *************************************************************************** +mDNSlocal mDNSu16 NToH16(mDNSu8 * bytes) +{ + return (mDNSu16)((mDNSu16)bytes[0] << 8 | (mDNSu16)bytes[1]); +} + +mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes) +{ + return (mDNSu32)((mDNSu32) bytes[0] << 24 | (mDNSu32) bytes[1] << 16 | (mDNSu32) bytes[2] << 8 | (mDNSu32)bytes[3]); +} + +// *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - MD5 Hash Functions #endif @@ -109,8 +64,8 @@ extern "C" { * 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 - * + * CommonDigest.h - APSL + * * md32_Common.h * ==================================================================== * Copyright (c) 1999-2002 The OpenSSL Project. All rights reserved. @@ -120,7 +75,7 @@ extern "C" { * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 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 @@ -173,21 +128,21 @@ extern "C" { * 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: @@ -202,10 +157,10 @@ extern "C" { * 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 + * 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 @@ -217,7 +172,7 @@ extern "C" { * 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 @@ -227,28 +182,14 @@ extern "C" { //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_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 @@ -261,7 +202,7 @@ void MD5_Transform(MD5_CTX *c, const unsigned char *b); # 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); +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 @@ -296,26 +237,26 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #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 +#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 +#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 @@ -446,11 +387,11 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #endif #ifndef HASH_LBLOCK -#define HASH_LBLOCK (HASH_CBLOCK/4) +#define HASH_LBLOCK (HASH_CBLOCK/4) #endif #ifndef HASH_LONG_LOG2 -#define HASH_LONG_LOG2 2 +#define HASH_LONG_LOG2 2 #endif /* @@ -459,41 +400,46 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #undef ROTATE #ifndef PEDANTIC # if 0 /* defined(_MSC_VER) */ -# define ROTATE(a,n) _lrotl(a,n) +# 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) +# 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) ) +/* 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) +# 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> - */ +/* + * Some GNU C inline assembler templates. Note that these are + * rotates by *constant* number of bits! But that's exactly + * what we need here... + * + * <appro@fy.chalmers.se> + */ +/* + * LLVM is more strict about compatibility of types between input & output constraints, + * but we want these to be rotations of 32 bits, not 64, so we explicitly drop the + * most significant bytes by casting to an unsigned int. + */ # if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) -# define ROTATE(a,n) ({ register unsigned int ret; \ - asm ( \ - "roll %1,%0" \ - : "=r"(ret) \ - : "I"(n), "0"(a) \ - : "cc"); \ - ret; \ - }) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "roll %1,%0" \ + : "=r" (ret) \ + : "I" (n), "0" ((unsigned int)a) \ + : "cc"); \ + ret; \ + }) # elif defined(__powerpc) || defined(__ppc) -# define ROTATE(a,n) ({ register unsigned int ret; \ - asm ( \ - "rlwinm %0,%1,%2,0,31" \ - : "=r"(ret) \ - : "r"(a), "I"(n)); \ - ret; \ - }) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "rlwinm %0,%1,%2,0,31" \ + : "=r" (ret) \ + : "r" (a), "I" (n)); \ + ret; \ + }) # endif # endif @@ -502,50 +448,50 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); * 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> */ +/* 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; \ - }) +# 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; \ - }) +# 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; \ - }) +# 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 */ +#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))) \ - ) +#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) \ - ) +#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: @@ -578,28 +524,28 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #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 +# 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) +# define HOST_FETCH32(p,l) LE_FETCH32(p) # elif defined(REVERSE_FETCH32) -# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# 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 +# 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) +# define HOST_FETCH32(p,l) BE_FETCH32(p) # elif defined(REVERSE_FETCH32) -# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) # endif # endif # endif @@ -611,77 +557,84 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); #endif #endif +// None of the invocations of the following macros actually use the result, +// so cast them to void to avoid any compiler warnings/errors about not using +// the result (e.g. when using clang). +// If the resultant values need to be used at some point, these must be changed. +#define HOST_c2l(c,l) ((void)_HOST_c2l(c,l)) +#define HOST_l2c(l,c) ((void)_HOST_l2c(l,c)) + #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; \ - } } +#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) +#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; \ - } } +#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) +#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 @@ -690,205 +643,205 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); */ 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) - { +{ + 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 + /* + * 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; - } + while (sw--) + { + mDNSPlatformMemCopy(p=c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED(c,p,1); + data+=HASH_CBLOCK; + len-=HASH_CBLOCK; + } #endif #endif #if defined(HASH_BLOCK_DATA_ORDER) - { - HASH_BLOCK_DATA_ORDER(c,data,sw); - sw*=HASH_CBLOCK; - data+=sw; - len-=sw; - } + { + 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; - } + } + + 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 ((((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); - } + { + mDNSPlatformMemCopy(c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED (c,c->data,1); + } #endif #endif #if defined(HASH_BLOCK_DATA_ORDER) - HASH_BLOCK_DATA_ORDER (c,data,1); + 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; +{ + 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' */ + /* 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:-) */ + if (j==0) p[i]=0; /* Yeah, but that's not the way to fix it:-) */ #endif - l=p[i]; + l=p[i]; #else - l = (j==0) ? 0 : p[i]; + l = (j==0) ? 0 : p[i]; #endif - HOST_p_c2l(cp,l,j); p[i++]=l; /* i is the next 'undefined word' */ + 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 (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; + 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; + p[HASH_LBLOCK-2]=c->Nl; + p[HASH_LBLOCK-1]=c->Nh; #endif - HASH_BLOCK_HOST_ORDER (c,p,1); + HASH_BLOCK_HOST_ORDER (c,p,1); #ifndef HASH_MAKE_STRING #error "HASH_MAKE_STRING must be defined!" #else - HASH_MAKE_STRING(c,md); + 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; - } + 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 @@ -905,7 +858,7 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c) * *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* + * Well, to be honest it should say that this *prevents* * performance degradation. * <appro@fy.chalmers.se> * Apparently there're LP64 compilers that generate better @@ -919,39 +872,39 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c) // from md5_locl.h (continued) /* -#define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) -#define G(x,y,z) (((x) & (z)) | ((y) & (~(z)))) -*/ + #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 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; };\ + 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; }; + 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; }; + 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; }; - + a+=((k)+(t)+I((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + // from md5_dgst.c @@ -964,105 +917,105 @@ int HASH_FINAL (unsigned char *md, HASH_CTX *c) #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; - } +{ + 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; - } - } +{ + 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 @@ -1070,127 +1023,125 @@ void md5_block_host_order (MD5_CTX *c, const void *data, int num) #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; +{ + 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 + /* 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] + 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; - } - } + 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 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++; - } - } +mDNSlocal const char *mDNSstrchr(const char *s, int c) +{ + while (1) + { + if (c == *s) return s; + if (!*s) return mDNSNULL; + s++; + } +} // skips all whitespace anywhere. // converts characters, four at a time, starting at (or after) @@ -1198,124 +1149,124 @@ static const char *mDNSstrchr(const char *s, int c) // 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); - } - - - // *************************************************************************** +mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize) +{ + int tarindex, state, ch; + const char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (mDNSisspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = mDNSstrchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((mDNSu32)tarindex >= targsize) + return (-1); + target[tarindex] = (mDNSu8)((pos - Base64) << 2); + } + state = 1; + break; + case 1: + if (target) { + if ((mDNSu32)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x0f) << 4); + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((mDNSu32)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x03) << 6); + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((mDNSu32)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + return -1; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)mDNSNULL; ch != '\0'; ch = *src++) + if (!mDNSisspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)mDNSNULL; ch != '\0'; ch = *src++) + if (!mDNSisspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} + + +// *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - API exported to mDNS Core #endif @@ -1328,137 +1279,288 @@ mDNSexport mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu3 #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 +mDNSlocal void DNSDigest_ConstructHMACKey(DomainAuthInfo *info, const mDNSu8 *key, mDNSu32 len) +{ + MD5_CTX k; + mDNSu8 buf[MD5_LEN]; + int i; + + // If key is longer than HMAC_LEN reset it to MD5(key) + if (len > HMAC_LEN) + { + MD5_Init(&k); + MD5_Update(&k, key, len); + MD5_Final(buf, &k); + key = buf; + len = MD5_LEN; + } + + // store key in pads + mDNSPlatformMemZero(info->keydata_ipad, HMAC_LEN); + mDNSPlatformMemZero(info->keydata_opad, HMAC_LEN); + mDNSPlatformMemCopy(info->keydata_ipad, key, len); + mDNSPlatformMemCopy(info->keydata_opad, key, len); + + // XOR key with ipad and opad values + for (i = 0; i < HMAC_LEN; i++) + { + info->keydata_ipad[i] ^= HMAC_IPAD; + info->keydata_opad[i] ^= HMAC_OPAD; + } + +} + +mDNSexport mDNSs32 DNSDigest_ConstructHMACKeyfromBase64(DomainAuthInfo *info, const char *b64key) +{ + mDNSu8 keybuf[1024]; + mDNSs32 keylen = DNSDigest_Base64ToBin(b64key, keybuf, sizeof(keybuf)); + if (keylen < 0) return(keylen); + DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen); + return(keylen); +} + +mDNSexport void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo *info, mDNSu16 tcode) +{ + AuthRecord tsig; + mDNSu8 *rdata, *const countPtr = (mDNSu8 *)&msg->h.numAdditionals; // Get existing numAdditionals value + mDNSu32 utc32; + mDNSu8 utc48[6]; + mDNSu8 digest[MD5_LEN]; + mDNSu8 *ptr = *end; + mDNSu32 len; + mDNSOpaque16 buf; + MD5_CTX c; + mDNSu16 numAdditionals = (mDNSu16)((mDNSu16)countPtr[0] << 8 | countPtr[1]); + + // Init MD5 context, digest inner key pad and message MD5_Init(&c); - MD5_Update(&c, info->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_Update(&c, info->keydata_ipad, HMAC_LEN); + MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg)); + + // Construct TSIG RR, digesting variables as apporpriate + mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + + // key name + AssignDomainName(&tsig.namestorage, &info->keyname); + MD5_Update(&c, info->keyname.c, DomainNameLength(&info->keyname)); + + // class + tsig.resrec.rrclass = kDNSQClass_ANY; + buf = mDNSOpaque16fromIntVal(kDNSQClass_ANY); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + + // ttl + tsig.resrec.rroriginalttl = 0; + MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl)); + + // alg name + AssignDomainName(&tsig.resrec.rdata->u.name, &HMAC_MD5_AlgName); + len = DomainNameLength(&HMAC_MD5_AlgName); + rdata = tsig.resrec.rdata->u.data + len; + MD5_Update(&c, HMAC_MD5_AlgName.c, len); + + // time + // get UTC (universal time), convert to 48-bit unsigned in network byte order + utc32 = (mDNSu32)mDNSPlatformUTC(); + if (utc32 == (unsigned)-1) { LogMsg("ERROR: DNSDigest_SignMessage - mDNSPlatformUTC returned bad time -1"); *end = mDNSNULL; } + utc48[0] = 0; + utc48[1] = 0; + utc48[2] = (mDNSu8)((utc32 >> 24) & 0xff); + utc48[3] = (mDNSu8)((utc32 >> 16) & 0xff); + utc48[4] = (mDNSu8)((utc32 >> 8) & 0xff); + utc48[5] = (mDNSu8)( utc32 & 0xff); + + mDNSPlatformMemCopy(rdata, utc48, 6); + rdata += 6; + MD5_Update(&c, utc48, 6); + + // 300 sec is fudge recommended in RFC 2485 + rdata[0] = (mDNSu8)((300 >> 8) & 0xff); + rdata[1] = (mDNSu8)( 300 & 0xff); + MD5_Update(&c, rdata, sizeof(mDNSOpaque16)); + rdata += sizeof(mDNSOpaque16); + + // digest error (tcode) and other data len (zero) - we'll add them to the rdata later + buf.b[0] = (mDNSu8)((tcode >> 8) & 0xff); + buf.b[1] = (mDNSu8)( tcode & 0xff); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // error + buf.NotAnInteger = 0; + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // other data len + + // finish the message & tsig var hash MD5_Final(digest, &c); - - // perform outer MD5 (outer key pad, inner digest) - MD5_Init(&c); - MD5_Update(&c, info->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; - } + + // perform outer MD5 (outer key pad, inner digest) + MD5_Init(&c); + MD5_Update(&c, info->keydata_opad, HMAC_LEN); + MD5_Update(&c, digest, MD5_LEN); + MD5_Final(digest, &c); + + // set remaining rdata fields + rdata[0] = (mDNSu8)((MD5_LEN >> 8) & 0xff); + rdata[1] = (mDNSu8)( MD5_LEN & 0xff); + rdata += sizeof(mDNSOpaque16); + mDNSPlatformMemCopy(rdata, digest, MD5_LEN); // MAC + rdata += MD5_LEN; + rdata[0] = msg->h.id.b[0]; // original ID + rdata[1] = msg->h.id.b[1]; + rdata[2] = (mDNSu8)((tcode >> 8) & 0xff); + rdata[3] = (mDNSu8)( tcode & 0xff); + rdata[4] = 0; // other data len + rdata[5] = 0; + rdata += 6; + + tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data); + *end = PutResourceRecordTTLJumbo(msg, ptr, &numAdditionals, &tsig.resrec, 0); + if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); *end = mDNSNULL; return; } + + // Write back updated numAdditionals value + countPtr[0] = (mDNSu8)(numAdditionals >> 8); + countPtr[1] = (mDNSu8)(numAdditionals & 0xFF); +} + +mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCacheRecord * lcr, DomainAuthInfo *info, mDNSu16 * rcode, mDNSu16 * tcode) +{ + mDNSu8 * ptr = (mDNSu8*) &lcr->r.resrec.rdata->u.data; + mDNSs32 now; + mDNSs32 then; + mDNSu8 thisDigest[MD5_LEN]; + mDNSu8 thatDigest[MD5_LEN]; + mDNSOpaque16 buf; + mDNSu8 utc48[6]; + mDNSs32 delta; + mDNSu16 fudge; + domainname * algo; + MD5_CTX c; + mDNSBool ok = mDNSfalse; + + // We only support HMAC-MD5 for now + + algo = (domainname*) ptr; + + if (!SameDomainName(algo, &HMAC_MD5_AlgName)) + { + LogMsg("ERROR: DNSDigest_VerifyMessage - TSIG algorithm not supported: %##s", algo->c); + *rcode = kDNSFlag1_RC_NotAuth; + *tcode = TSIG_ErrBadKey; + ok = mDNSfalse; + goto exit; + } + + ptr += DomainNameLength(algo); + + // Check the times + + now = mDNSPlatformUTC(); + if (now == -1) + { + LogMsg("ERROR: DNSDigest_VerifyMessage - mDNSPlatformUTC returned bad time -1"); + *rcode = kDNSFlag1_RC_NotAuth; + *tcode = TSIG_ErrBadTime; + ok = mDNSfalse; + goto exit; + } + + // Get the 48 bit time field, skipping over the first word + + utc48[0] = *ptr++; + utc48[1] = *ptr++; + utc48[2] = *ptr++; + utc48[3] = *ptr++; + utc48[4] = *ptr++; + utc48[5] = *ptr++; + + then = (mDNSs32)NToH32(utc48 + sizeof(mDNSu16)); + + fudge = NToH16(ptr); + + ptr += sizeof(mDNSu16); + + delta = (now > then) ? now - then : then - now; + + if (delta > fudge) + { + LogMsg("ERROR: DNSDigest_VerifyMessage - time skew > %d", fudge); + *rcode = kDNSFlag1_RC_NotAuth; + *tcode = TSIG_ErrBadTime; + ok = mDNSfalse; + goto exit; + } + + // MAC size + + ptr += sizeof(mDNSu16); + + // MAC + + mDNSPlatformMemCopy(thatDigest, ptr, MD5_LEN); + + // Init MD5 context, digest inner key pad and message + + MD5_Init(&c); + MD5_Update(&c, info->keydata_ipad, HMAC_LEN); + MD5_Update(&c, (mDNSu8*) msg, (unsigned long)(end - (mDNSu8*) msg)); + + // Key name + + MD5_Update(&c, lcr->r.resrec.name->c, DomainNameLength(lcr->r.resrec.name)); + + // Class name + + buf = mDNSOpaque16fromIntVal(lcr->r.resrec.rrclass); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + + // TTL + + MD5_Update(&c, (mDNSu8*) &lcr->r.resrec.rroriginalttl, sizeof(lcr->r.resrec.rroriginalttl)); + + // Algorithm + + MD5_Update(&c, algo->c, DomainNameLength(algo)); + + // Time + + MD5_Update(&c, utc48, 6); + + // Fudge + + buf = mDNSOpaque16fromIntVal(fudge); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + + // Digest error and other data len (both zero) - we'll add them to the rdata later + + buf.NotAnInteger = 0; + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // error + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // other data len + + // Finish the message & tsig var hash + + MD5_Final(thisDigest, &c); + + // perform outer MD5 (outer key pad, inner digest) + + MD5_Init(&c); + MD5_Update(&c, info->keydata_opad, HMAC_LEN); + MD5_Update(&c, thisDigest, MD5_LEN); + MD5_Final(thisDigest, &c); + + if (!mDNSPlatformMemSame(thisDigest, thatDigest, MD5_LEN)) + { + LogMsg("ERROR: DNSDigest_VerifyMessage - bad signature"); + *rcode = kDNSFlag1_RC_NotAuth; + *tcode = TSIG_ErrBadSig; + ok = mDNSfalse; + goto exit; + } + + // set remaining rdata fields + ok = mDNStrue; + +exit: + + return ok; +} + #ifdef __cplusplus } diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c index 141c3bc9ca..45dbb7cbd3 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c @@ -5,330 +5,315 @@ * 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 + File: GenLinkedList.c -Revision 1.3 2004/04/22 21:14:42 cheshire -Fix comment spelling mistake + Contains: implementation of generic linked lists. -Revision 1.2 2004/02/05 07:41:08 cheshire -Add Log header - -*/ - -#pragma ident "%Z%%M% %I% %E% SMI" + Version: 1.0 + Tabs: 4 spaces + */ #include "GenLinkedList.h" // Return the link pointer contained within element e at offset o. -#define GETLINK( e, o) ( *(void**)((char*) (e) + (o)) ) +#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)) +#define ASSIGNLINK( e, l, o) ( *((void**)((char*) (e) + (o))) = (l)) // GenLinkedList ///////////////////////////////////////////////////////////// -void InitLinkedList( GenLinkedList *pList, size_t linkOffset) +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; + pList->Head = NULL; + pList->Tail = NULL; + pList->LinkOffset = linkOffset; } -void AddToTail( GenLinkedList *pList, void *elem) +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); + if ( pList->Tail) { + ASSIGNLINK( pList->Tail, elem, pList->LinkOffset); + } else + pList->Head = elem; + ASSIGNLINK( elem, NULL, pList->LinkOffset); - pList->Tail = elem; + pList->Tail = elem; } -void AddToHead( GenLinkedList *pList, void *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; + ASSIGNLINK( elem, pList->Head, pList->LinkOffset); + if ( pList->Tail == NULL) + pList->Tail = elem; - pList->Head = elem; + pList->Head = elem; } -int RemoveFromList( GenLinkedList *pList, void *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; + 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) +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; + 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) +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; + pList->Head = NULL; + pList->Tail = NULL; + pList->FwdLinkOffset = fwdLinkOffset; + pList->BackLinkOffset = backLinkOffset; } -void DLLAddToHead( GenDoubleLinkedList *pList, void *elem) +void DLLAddToHead( GenDoubleLinkedList *pList, void *elem) /* Add a linked list element to the head of the list. */ { -void *pNext; + void *pNext; - pNext = pList->Head; + pNext = pList->Head; - // fix up the forward links - ASSIGNLINK( elem, pList->Head, pList->FwdLinkOffset); - pList->Head = elem; + // 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); + // 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) +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); + 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)) ) +#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); -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; + GETOFFSET( elem, linkOffset) = link ? (size_t) link - (size_t) elem : 0; } -void *GetHeadPtr( GenLinkedOffsetList *pList) +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; + return pList->Head ? ( (char*) (pList) + pList->Head) : NULL; } -void *GetTailPtr( GenLinkedOffsetList *pList) +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; + return pList->Tail ? ( (char*) (pList) + pList->Tail) : NULL; } -void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem) +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; + size_t nextOffset; - nextOffset = GETOFFSET( elem, pList->LinkOffset); + nextOffset = GETOFFSET( elem, pList->LinkOffset); - return nextOffset ? (char*) elem + nextOffset : NULL; + return nextOffset ? (char*) elem + nextOffset : NULL; } -void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset) +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; + pList->Head = 0; + pList->Tail = 0; + pList->LinkOffset = linkOffset; } -void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem) +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); + 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; + pList->Tail = (size_t) elem - (size_t) pList; } -void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem) +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; + 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; + pList->Head = (size_t) elem - (size_t) pList; } -int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem) +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; + 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) +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; + 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 index 71863346b0..2d0ada6dcd 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h @@ -5,34 +5,15 @@ * 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__ @@ -41,69 +22,69 @@ Add Log header #include <stddef.h> -struct GenLinkedList +struct GenLinkedList { - void *Head, - *Tail; - size_t LinkOffset; + void *Head, + *Tail; + size_t LinkOffset; }; -typedef struct GenLinkedList GenLinkedList; +typedef struct GenLinkedList GenLinkedList; -void InitLinkedList( GenLinkedList *pList, size_t linkOffset); +void InitLinkedList( GenLinkedList *pList, size_t linkOffset); -void AddToHead( GenLinkedList *pList, void *elem); -void AddToTail( GenLinkedList *pList, void *elem); +void AddToHead( GenLinkedList *pList, void *elem); +void AddToTail( GenLinkedList *pList, void *elem); -int RemoveFromList( GenLinkedList *pList, void *elem); +int RemoveFromList( GenLinkedList *pList, void *elem); -int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem); +int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem); -struct GenDoubleLinkedList +struct GenDoubleLinkedList { - void *Head, - *Tail; - size_t FwdLinkOffset, - BackLinkOffset; + void *Head, + *Tail; + size_t FwdLinkOffset, + BackLinkOffset; }; -typedef struct GenDoubleLinkedList GenDoubleLinkedList; +typedef struct GenDoubleLinkedList GenDoubleLinkedList; -void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, - size_t backLinkOffset); +void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, + size_t backLinkOffset); -void DLLAddToHead( GenDoubleLinkedList *pList, void *elem); +void DLLAddToHead( GenDoubleLinkedList *pList, void *elem); -void DLLRemoveFromList( 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 +struct GenLinkedOffsetList { - size_t Head, - Tail; - size_t LinkOffset; + size_t Head, + Tail; + size_t LinkOffset; }; -typedef struct GenLinkedOffsetList GenLinkedOffsetList; +typedef struct GenLinkedOffsetList GenLinkedOffsetList; -void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset); +void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset); -void *GetHeadPtr( GenLinkedOffsetList *pList); -void *GetTailPtr( GenLinkedOffsetList *pList); -void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem); +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); +void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem); +void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem); -int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem); +int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem); -int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem); +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 index 8fd9bcaf64..128bb637b4 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile @@ -20,6 +20,7 @@ # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2016 Toomas Soome <tsoome@me.com> # PROG= mdnsd @@ -32,15 +33,15 @@ 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 + uDNS.o uds_daemon.o CryptoAlg.o anonymous.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 \ +MDNSFLAGS= -DNOT_HAVE_SA_LEN \ + -DLOG_PERROR=0 -DHAVE_SOLARIS -DTARGET_OS_SOLARIS \ -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME \ -DHAVE_IPV6=1 -Dasm=__asm -DMDNSD_NOROOT \ -DPID_FILE=\"\" -DMDNSD_USER=\"noaccess\" \ diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c index 00a428f934..cf8e404637 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c @@ -5,134 +5,195 @@ * 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 <stdio.h> // Needed for fopen() etc. +#include <unistd.h> // Needed for close() +#include <string.h> // Needed for strlen() etc. +#include <errno.h> // Needed for errno etc. +#include <sys/socket.h> // Needed for socket() etc. +#include <netinet/in.h> // Needed for sockaddr_in +#include <syslog.h> -#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "DNSCommon.h" #include "PlatformCommon.h" #ifdef NOT_HAVE_SOCKLEN_T - typedef unsigned int 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; - } +// Bind a UDP socket to find the source address to a destination +mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst) +{ + union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr; + socklen_t len = sizeof(addr); + socklen_t inner_len = 0; + int sock = socket(AF_INET, SOCK_DGRAM, 0); + src->type = mDNSAddrType_None; + if (sock == -1) return; + if (dst->type == mDNSAddrType_IPv4) + { + inner_len = sizeof(addr.a4); + #ifndef NOT_HAVE_SA_LEN + addr.a4.sin_len = inner_len; + #endif + addr.a4.sin_family = AF_INET; + addr.a4.sin_port = 1; // Not important, any port will do + addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger; + } + else if (dst->type == mDNSAddrType_IPv6) + { + inner_len = sizeof(addr.a6); + #ifndef NOT_HAVE_SA_LEN + addr.a6.sin6_len = inner_len; + #endif + addr.a6.sin6_family = AF_INET6; + addr.a6.sin6_flowinfo = 0; + addr.a6.sin6_port = 1; // Not important, any port will do + addr.a6.sin6_addr = *(struct in6_addr*)&dst->ip.v6; + addr.a6.sin6_scope_id = 0; + } + else return; + + if ((connect(sock, &addr.s, inner_len)) < 0) + { if (errno != ENETUNREACH) LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno)); goto exit; } + + if ((getsockname(sock, &addr.s, &len)) < 0) + { LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; } + + src->type = dst->type; + if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr; + else src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr; +exit: + close(sock); +} // dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f) - { - char buf[32+1+MAX_ESCAPED_DOMAIN_NAME]; // Option name, one space, option value - unsigned int len = strlen(option); - if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; } - fseek(f, 0, SEEK_SET); // set position to beginning of stream - while (fgets(buf, sizeof(buf), f)) // Read at most sizeof(buf)-1 bytes from file, and append '\0' C-string terminator - { - if (!strncmp(buf, option, len)) - { - strncpy(dst, buf + len + 1, MAX_ESCAPED_DOMAIN_NAME-1); - if (dst[MAX_ESCAPED_DOMAIN_NAME-1]) dst[MAX_ESCAPED_DOMAIN_NAME-1] = '\0'; - len = strlen(dst); - if (len && dst[len-1] == '\n') dst[len-1] = '\0'; // chop newline - return mDNStrue; - } - } - debugf("Option %s not set", option); - return mDNSfalse; - } +{ + 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); - } +{ + char buf[MAX_ESCAPED_DOMAIN_NAME] = ""; + mStatus err; + FILE *f = fopen(filename, "r"); + + if (hostname) hostname->c[0] = 0; + if (domain) domain->c[0] = 0; + if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse; + + if (f) + { + if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue; + if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf; + if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf; + buf[0] = 0; + GetConfigOption(buf, "secret-64", f); // failure means no authentication + fclose(f); + f = NULL; + } + else + { + if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened."); + return; + } + + if (domain && domain->c[0] && buf[0]) + { + DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info)); + // for now we assume keyname = service reg domain and we use same key for service and hostname registration + err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, mDNSfalse); + if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c); + } + + return; + +badf: + LogMsg("ERROR: malformatted config file"); + if (f) fclose(f); +} + +#if MDNS_DEBUGMSGS +mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg) +{ + fprintf(stderr,"%s\n", msg); + fflush(stderr); +} +#endif + +mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, mDNSLogLevel_t loglevel) +{ +#if APPLE_OSX_mDNSResponder && LogTimeStamps + extern mDNS mDNSStorage; + extern mDNSu32 mDNSPlatformClockDivisor; + mDNSs32 t = mDNSStorage.timenow ? mDNSStorage.timenow : mDNSPlatformClockDivisor ? mDNS_TimeNow_NoLock(&mDNSStorage) : 0; + int ms = ((t < 0) ? -t : t) % 1000; +#endif + + if (mDNS_DebugMode) // In debug mode we write to stderr + { +#if APPLE_OSX_mDNSResponder && LogTimeStamps + if (ident && ident[0] && mDNSPlatformClockDivisor) + fprintf(stderr,"%8d.%03d: %s\n", (int)(t/1000), ms, buffer); + else +#endif + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } + else // else, in production mode, we write to syslog + { + static int log_inited = 0; + + int syslog_level = LOG_ERR; + switch (loglevel) + { + case MDNS_LOG_MSG: syslog_level = LOG_ERR; break; + case MDNS_LOG_OPERATION: syslog_level = LOG_WARNING; break; + case MDNS_LOG_SPS: syslog_level = LOG_NOTICE; break; + case MDNS_LOG_INFO: syslog_level = LOG_INFO; break; + case MDNS_LOG_DEBUG: syslog_level = LOG_DEBUG; break; + default: + fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel); + fflush(stderr); + } + + if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; } + +#if APPLE_OSX_mDNSResponder && LogTimeStamps + if (ident && ident[0] && mDNSPlatformClockDivisor) + syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer); + else +#elif APPLE_OSX_mDNSResponder + mDNSPlatformLogToFile(syslog_level, buffer); +#else + syslog(syslog_level, "%s", buffer); +#endif + } +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h index 98f0e85e09..2a068711e0 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h @@ -5,33 +5,14 @@ * 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 index 9bf03a9548..cfd9d0a30e 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c @@ -5,119 +5,28 @@ * 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() + File: daemon.c -Revision 1.13 2004/08/11 01:59:41 cheshire -Remove "mDNS *globalInstance" parameter from udsserver_init() + Contains: main & associated Application layer for mDNSResponder on Linux. -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" +#if __APPLE__ +// In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated” +// error, which prevents compilation because we build with "-Werror". +// Since this is supposed to be portable cross-platform code, we don't care that daemon is +// deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message. +#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou +#endif #include <stdio.h> #include <string.h> @@ -129,12 +38,16 @@ Add support for mDNSResponder on Linux. #include <pwd.h> #include <sys/types.h> +#if __APPLE__ +#undef daemon +extern int daemon(int, int); +#endif + #include "mDNSEmbeddedAPI.h" -#include "mDNSDebug.h" #include "mDNSPosix.h" +#include "mDNSUNP.h" // For daemon() #include "uds_daemon.h" #include "PlatformCommon.h" -#include "mDNSUNP.h" #ifndef MDNSD_USER #define MDNSD_USER "nobody" @@ -146,226 +59,225 @@ static domainname DynDNSHostname; #define RR_CACHE_SIZE 500 static CacheEntity gRRCache[RR_CACHE_SIZE]; - -extern const char mDNSResponderVersionString[]; +static mDNS_PlatformSupport PlatformStorage; + +mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) +{ + (void)m; // Unused + if (result == mStatus_NoError) + { + // On successful registration of dot-local mDNS host name, daemon may want to check if + // any name conflict and automatic renaming took place, and if so, record the newly negotiated + // name in persistent storage for next time. It should also inform the user of the name change. + // On Mac OS X we store the current dot-local mDNS host name in the SCPreferences store, + // and notify the user with a CFUserNotification. + } + else if (result == mStatus_ConfigChanged) + { + udsserver_handle_configchange(m); + } + else if (result == mStatus_GrowCache) + { + // Allocate another chunk of cache storage + CacheEntity *storage = malloc(sizeof(CacheEntity) * RR_CACHE_SIZE); + if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); + } +} + +// %%% Reconfigure() probably belongs in the platform support layer (mDNSPosix.c), not the daemon cde +// -- all client layers running on top of mDNSPosix.c need to handle network configuration changes, +// not only the Unix Domain Socket Daemon static void Reconfigure(mDNS *m) - { - mDNSAddr DynDNSIP; - mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL); - mDNS_DeleteDNSServers(m); - if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) - LogMsgNoIdent("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); - } +{ + mDNSAddr DynDNSIP; + const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };; + mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL); + if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) + LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); + ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL); + mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy); + if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); + if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL); + mDNS_ConfigChanged(m); +} // Do appropriate things at startup with command line arguments. Calls exit() if unhappy. -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); } +mDNSlocal void ParseCmdLinArgs(int argc, char **argv) +{ + if (argc > 1) + { + if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue; + else printf("Usage: %s [-debug]\n", argv[0]); + } + + 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); + 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); +mDNSlocal void ToggleLog(void) +{ + mDNS_LoggingEnabled = !mDNS_LoggingEnabled; +} - ParseCmdLinArgs(argc, argv); +mDNSlocal void ToggleLogPacket(void) +{ + mDNS_PacketLoggingEnabled = !mDNS_PacketLoggingEnabled; +} - 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 +mDNSlocal void DumpStateLog(mDNS *const m) +// Dump a little log of what we've been up to. +{ + LogMsg("---- BEGIN STATE LOG ----"); + udsserver_info(m); + LogMsg("---- END STATE LOG ----"); +} + +mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit. +{ + sigset_t signals; + mDNSBool gotData = mDNSfalse; + + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + mDNSPosixListenForSignalInEventLoop(SIGUSR1); + mDNSPosixListenForSignalInEventLoop(SIGUSR2); + mDNSPosixListenForSignalInEventLoop(SIGINFO); + 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, SIGINFO)) DumpStateLog(m); + if (sigismember(&signals, SIGUSR1)) ToggleLog(); + if (sigismember(&signals, SIGUSR2)) ToggleLogPacket(); + // SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up. + if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring"); + if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break; + } + return EINTR; +} + +int main(int argc, char **argv) +{ + mStatus err; + + ParseCmdLinArgs(argc, argv); + + LogInfo("%s starting", mDNSResponderVersionString); + + err = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, + mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + + if (mStatus_NoError == err) + err = udsserver_init(mDNSNULL, 0); + + Reconfigure(&mDNSStorage); + + // Now that we're finished with anything privileged, switch over to running as "nobody" + if (mStatus_NoError == err) + { + const struct passwd *pw = getpwnam(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; - } + { + 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"); + LogMsg("WARNING: mdnsd continuing as root because user \""MDNSD_USER"\" does not exist"); #endif - } + } + + if (mStatus_NoError == err) + err = MainLoop(&mDNSStorage); + + LogInfo("%s stopping", mDNSResponderVersionString); - if (mStatus_NoError == err) - err = MainLoop(&mDNSRecord); - - LogMsgIdent(mDNSResponderVersionString, "stopping"); + mDNS_Close(&mDNSStorage); - mDNS_Close(&mDNSRecord); + if (udsserver_exit() < 0) + LogMsg("ExitCallback: udsserver_exit failed"); - if (udsserver_exit() < 0) - LogMsg("ExitCallback: udsserver_exit failed"); - #if MDNS_DEBUGMSGS > 0 - printf("mDNSResponder exiting normally with %ld\n", err); + printf("mDNSResponder exiting normally with %ld\n", err); #endif - - return err; - } - -// uds_daemon support //////////////////////////////////////////////////////////// -#undef LogMalloc + return err; +} -#if MDNS_MALLOC_DEBUGGING >= 2 -#define LogMalloc LogMsg -#else -#define LogMalloc(ARGS...) ((void)0) -#endif +// uds_daemon support //////////////////////////////////////////////////////////// -mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context) +mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data) /* 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; - } +{ + // Depends on the fact that udsEventCallback == mDNSPosixEventCallback + (void) platform_data; + return mDNSPosixAddFDToEventLoop(fd, callback, context); +} + +int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data) +{ + (void) platform_data; + return recv(fd, buf, len, flags); +} + +mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor +{ + mStatus err = mDNSPosixRemoveFDFromEventLoop(fd); + (void) platform_data; + close(fd); + return err; +} mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) - { - (void)m; - (void)delay; - // No-op, for now - } - -#if 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 +{ + (void)m; + (void)delay; + // No-op, for now +} + +#if _BUILDING_XCODE_PROJECT_ +// If the process crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5; +asm (".desc ___crashreporter_info__, 0x10"); +#endif // For convenience when using the "strings" command, this is the last thing in the file #if mDNSResponderVersion > 1 -mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ") "; +mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; #elif MDNS_VERSIONSTR_NODTS -mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder (Engineering Build) "; +mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)"; #else -mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ") "; +mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")"; #endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/THIRDPARTYLICENSE b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/THIRDPARTYLICENSE index a4d739e67a..f4589044ee 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/THIRDPARTYLICENSE +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/THIRDPARTYLICENSE @@ -1,13 +1,13 @@ - * 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. +The majority of the source code in the mDNSResponder project is licensed +under the terms of the Apache License, Version 2.0, available from: + <http://www.apache.org/licenses/LICENSE-2.0> + +To accommodate license compatibility with the widest possible range +of client code licenses, the shared library code, which is linked +at runtime into the same address space as the client using it, is +licensed under the terms of the "Three-Clause BSD License". + +The Linux Name Service Switch code, contributed by National ICT +Australia Ltd (NICTA) is licensed under the terms of the NICTA Public +Software Licence (which is substantially similar to the "Three-Clause +BSD License", with some additional language pertaining to Australian law). diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/anonymous.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/anonymous.c new file mode 100644 index 0000000000..94b102ecb7 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/anonymous.c @@ -0,0 +1,597 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mDNSEmbeddedAPI.h" +#include "CryptoAlg.h" +#include "anonymous.h" +#include "DNSCommon.h" + +// Define ANONYMOUS_DISABLED to remove all the anonymous functionality +// and use the stub functions implemented later in this file. + +#ifndef ANONYMOUS_DISABLED + +#define ANON_NSEC3_ITERATIONS 1 + +mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt) +{ + const mDNSu8 *ptr; + rdataNSEC3 *nsec3 = (rdataNSEC3 *)rr->rdata->u.data; + mDNSu8 *tmp, *nxt; + unsigned short iter = ANON_NSEC3_ITERATIONS; + int hlen; + const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; + + // Construct the RDATA first and construct the owner name based on that. + ptr = (const mDNSu8 *)&salt; + debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr[0], ptr[1], ptr[2], ptr[3], rr->name->c); + + // Set the RDATA + nsec3->alg = SHA1_DIGEST_TYPE; + nsec3->flags = 0; + nsec3->iterations = swap16(iter); + nsec3->saltLength = 4; + tmp = (mDNSu8 *)&nsec3->salt; + *tmp++ = ptr[0]; + *tmp++ = ptr[1]; + *tmp++ = ptr[2]; + *tmp++ = ptr[3]; + + // hashLength, nxt, bitmap + *tmp++ = SHA1_HASH_LENGTH; // hash length + nxt = tmp; + tmp += SHA1_HASH_LENGTH; + *tmp++ = 0; // window number + *tmp++ = NSEC_MCAST_WINDOW_SIZE; // window length + mDNSPlatformMemZero(tmp, NSEC_MCAST_WINDOW_SIZE); + tmp[kDNSType_PTR >> 3] |= 128 >> (kDNSType_PTR & 7); + + // Hash the base service name + salt + AnonData + if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen)) + { + LogMsg("InitializeNSEC3Record: NSEC3HashName failed for ##s", rr->name->c); + return mDNSfalse; + } + if (hlen != SHA1_HASH_LENGTH) + { + LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen); + return mDNSfalse; + } + mDNSPlatformMemCopy(nxt, hashName, hlen); + + return mDNStrue; +} + +mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSu8 *AnonData, int len, mDNSu32 salt) +{ + ResourceRecord *rr; + int dlen; + domainname *name; + + // We are just allocating an RData which has StandardAuthRDSize + if (StandardAuthRDSize < MCAST_NSEC3_RDLENGTH) + { + LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize, MCAST_NSEC3_RDLENGTH); + return mDNSNULL; + } + + dlen = DomainNameLength(service); + + // Allocate space for the name and RData. + rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + dlen + sizeof(RData)); + if (!rr) + return mDNSNULL; + name = (domainname *)((mDNSu8 *)rr + sizeof(ResourceRecord)); + rr->RecordType = kDNSRecordTypePacketAuth; + rr->InterfaceID = mDNSInterface_Any; + rr->name = (const domainname *)name; + rr->rrtype = kDNSType_NSEC3; + rr->rrclass = kDNSClass_IN; + rr->rroriginalttl = kStandardTTL; + rr->rDNSServer = mDNSNULL; + rr->rdlength = MCAST_NSEC3_RDLENGTH; + rr->rdestimate = MCAST_NSEC3_RDLENGTH; + rr->rdata = (RData *)((mDNSu8 *)rr->name + dlen); + + AssignDomainName(name, service); + if (!InitializeNSEC3Record(rr, AnonData, len, salt)) + { + mDNSPlatformMemFree(rr); + return mDNSNULL; + } + return rr; +} + +mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const ResourceRecord *rr) +{ + int len; + domainname *name; + ResourceRecord *nsec3rr; + + if (rr->rdlength < MCAST_NSEC3_RDLENGTH) + { + LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr->rdlength, MCAST_NSEC3_RDLENGTH); + return mDNSNULL; + } + // Allocate space for the name and the rdata along with the ResourceRecord + len = DomainNameLength(rr->name); + nsec3rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + len + sizeof(RData)); + if (!nsec3rr) + return mDNSNULL; + + *nsec3rr = *rr; + name = (domainname *)((mDNSu8 *)nsec3rr + sizeof(ResourceRecord)); + nsec3rr->name = (const domainname *)name; + AssignDomainName(name, rr->name); + + nsec3rr->rdata = (RData *)((mDNSu8 *)nsec3rr->name + len); + mDNSPlatformMemCopy(nsec3rr->rdata->u.data, rr->rdata->u.data, rr->rdlength); + + si->nsec3RR = nsec3rr; + + return nsec3rr; +} + +// When a service is started or a browse is started with the Anonymous data, we allocate a new random +// number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and +// the anonymous data. +// +// If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can +// check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received. +mDNSexport AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *data, int len, const ResourceRecord *rr) +{ + AnonymousInfo *ai; + ai = (AnonymousInfo *)mDNSPlatformMemAllocate(sizeof(AnonymousInfo)); + if (!ai) + { + return mDNSNULL; + } + mDNSPlatformMemZero(ai, sizeof(AnonymousInfo)); + if (rr) + { + if (!CopyNSEC3ResourceRecord(ai, rr)) + { + mDNSPlatformMemFree(ai); + return mDNSNULL; + } + return ai; + } + ai->salt = mDNSRandom(0xFFFFFFFF); + ai->AnonData = mDNSPlatformMemAllocate(len); + if (!ai->AnonData) + { + mDNSPlatformMemFree(ai); + return mDNSNULL; + } + ai->AnonDataLen = len; + mDNSPlatformMemCopy(ai->AnonData, data, len); + ai->nsec3RR = ConstructNSEC3Record(service, data, len, ai->salt); + if (!ai->nsec3RR) + { + mDNSPlatformMemFree(ai); + return mDNSNULL; + } + return ai; +} + +mDNSexport void FreeAnonInfo(AnonymousInfo *ai) +{ + if (ai->nsec3RR) + mDNSPlatformMemFree(ai->nsec3RR); + if (ai->AnonData) + mDNSPlatformMemFree(ai->AnonData); + mDNSPlatformMemFree(ai); +} + +mDNSexport void ReInitAnonInfo(AnonymousInfo **AnonInfo, const domainname *name) +{ + if (*AnonInfo) + { + AnonymousInfo *ai = *AnonInfo; + *AnonInfo = AllocateAnonInfo(name, ai->AnonData, ai->AnonDataLen, mDNSNULL); + if (!(*AnonInfo)) + *AnonInfo = ai; + else + FreeAnonInfo(ai); + } +} + +// This function should be used only if you know that the question and +// the resource record belongs to the same set. The main usage is +// in ProcessQuery where we find the question to be part of the same +// set as the resource record, but it needs the AnonData to be +// initialized so that it can walk the cache records to see if they +// answer the question. +mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion) +{ + if (!q->AnonInfo || !rr->AnonInfo) + { + LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo); + return; + } + + debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo); + if (ForQuestion) + { + if (!q->AnonInfo->AnonData) + { + q->AnonInfo->AnonData = mDNSPlatformMemAllocate(rr->AnonInfo->AnonDataLen); + if (!q->AnonInfo->AnonData) + return; + } + mDNSPlatformMemCopy(q->AnonInfo->AnonData, rr->AnonInfo->AnonData, rr->AnonInfo->AnonDataLen); + q->AnonInfo->AnonDataLen = rr->AnonInfo->AnonDataLen; + } + else + { + if (!rr->AnonInfo->AnonData) + { + rr->AnonInfo->AnonData = mDNSPlatformMemAllocate(q->AnonInfo->AnonDataLen); + if (!rr->AnonInfo->AnonData) + return; + } + mDNSPlatformMemCopy(rr->AnonInfo->AnonData, q->AnonInfo->AnonData, q->AnonInfo->AnonDataLen); + rr->AnonInfo->AnonDataLen = q->AnonInfo->AnonDataLen; + } +} + +// returns -1 if the caller should ignore the result +// returns 1 if the record answers the question +// returns 0 if the record does not answer the question +mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + mDNSexport mDNS mDNSStorage; + ResourceRecord *nsec3RR; + int i; + AnonymousInfo *qai, *rai; + mDNSu8 *AnonData; + int AnonDataLen; + rdataNSEC3 *nsec3; + int hlen; + const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; + int nxtLength; + mDNSu8 *nxtName; + + debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c); + + // Currently only PTR records can have anonymous information + if (q->qtype != kDNSType_PTR) + { + return -1; + } + + // We allow anonymous questions to be answered by both normal services (without the + // anonymous information) and anonymous services that are part of the same set. And + // normal questions discover normal services and all anonymous services. + // + // The three cases have been enumerated clearly even though they all behave the + // same way. + if (!q->AnonInfo) + { + debugf("AnonInfoAnswersQuestion: not a anonymous type question"); + if (!rr->AnonInfo) + { + // case 1 + return -1; + } + else + { + // case 2 + debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q->qname.c, rr->name->c); + return -1; + } + } + else + { + // case 3 + if (!rr->AnonInfo) + { + debugf("AnonInfoAnswersQuestion: not a anonymous type record"); + return -1; + } + } + + // case 4: We have the anonymous information both in the question and the record. We need + // two sets of information to validate. + // + // 1) Anonymous data that identifies the set/group + // 2) NSEC3 record that contains the hash and the salt + // + // If the question is a remote one, it does not have the anonymous information to validate (just + // the NSEC3 record) and hence the anonymous data should come from the local resource record. If the + // question is local, it can come from either of them and if there is a mismatch between the + // question and record, it won't validate. + + qai = q->AnonInfo; + rai = rr->AnonInfo; + + if (qai->AnonData && rai->AnonData) + { + // Before a cache record is created, if there is a matching question i.e., part + // of the same set, then when the cache is created we also set the anonymous + // information. Otherwise, the cache record contains just the NSEC3 record and we + // won't be here for that case. + // + // It is also possible that a local question is matched against the local AuthRecord + // as that is also the case for which the AnonData would be non-NULL for both. + // We match questions against AuthRecords (rather than the cache) for LocalOnly case and + // to see whether a .local query should be suppressed or not. The latter never happens + // because PTR queries are never suppressed. + + // If they don't belong to the same anonymous set, then no point in validating. + if ((qai->AnonDataLen != rai->AnonDataLen) || + mDNSPlatformMemCmp(qai->AnonData, rai->AnonData, qai->AnonDataLen) != 0) + { + debugf("AnonInfoAnswersQuestion: AnonData mis-match for record %s question %##s ", + RRDisplayString(&mDNSStorage, rr), q->qname.c); + return 0; + } + // AnonData matches i.e they belong to the same group and the same service. + LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q->qname.c, + rr->name->c); + return 1; + } + else + { + debugf("AnonInfoAnswersQuestion: question %p, record %p", qai->AnonData, rai->AnonData); + } + + if (qai->AnonData) + { + // If there is AnonData, then this is a local question. The + // NSEC3 RR comes from the resource record which could be part + // of the cache or local auth record. The cache entry could + // be from a remote host or created when we heard our own + // announcements. In any case, we use that to see if it matches + // the question. + AnonData = qai->AnonData; + AnonDataLen = qai->AnonDataLen; + nsec3RR = rai->nsec3RR; + } + else + { + // Remote question or hearing our own question back + AnonData = rai->AnonData; + AnonDataLen = rai->AnonDataLen; + nsec3RR = qai->nsec3RR; + } + + if (!AnonData || !nsec3RR) + { + // AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for + // that too and we can end up here for that case. + debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData, nsec3RR, + q->qname.c, RRDisplayString(&mDNSStorage, rr)); + return 0; + } + debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q->qname.c, RRDisplayString(&mDNSStorage, nsec3RR)); + + + nsec3 = (rdataNSEC3 *)nsec3RR->rdata->u.data; + + if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen)) + { + LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for ##s", nsec3RR->name->c); + return mDNSfalse; + } + if (hlen != SHA1_HASH_LENGTH) + { + LogMsg("AnonInfoAnswersQuestion: hlen wrong %d", hlen); + return mDNSfalse; + } + + NSEC3Parse(nsec3RR, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL); + + if (hlen != nxtLength) + { + LogMsg("AnonInfoAnswersQuestion: ERROR!! hlen %d not same as nxtLength %d", hlen, nxtLength); + return mDNSfalse; + } + + for (i = 0; i < nxtLength; i++) + { + if (nxtName[i] != hashName[i]) + { + debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName[i+1], hashName[i], i); + return 0; + } + } + LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage, nsec3RR), q->qname.c, DNSTypeName(q->qtype)); + return 1; +} + +// Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order. +// Similarly we also parse the NSEC3 records in order and this mapping to the questions and records +// respectively. +mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name) +{ + CacheRecord *cr; + CacheRecord **prev = nsec3; + + (void) m; + + for (cr = *nsec3; cr; cr = cr->next) + { + if (SameDomainName(cr->resrec.name, name)) + { + debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m, cr), name->c); + *prev = cr->next; + cr->next = mDNSNULL; + return cr; + } + prev = &cr->next; + } + return mDNSNULL; +} + +mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q) +{ + CacheRecord *nsec3CR; + + if (q->qtype != kDNSType_PTR) + return; + + nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, &q->qname); + if (nsec3CR) + { + q->AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec); + if (q->AnonInfo) + { + debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)", + RRDisplayString(m, q->AnonInfo->nsec3RR), q->qname.c, DNSTypeName(q->qtype)); + } + ReleaseCacheRecord(m, nsec3CR); + } +} + +mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr) +{ + CacheRecord *nsec3CR; + + if (!(*McastNSEC3Records)) + return; + + // If already initialized or not a PTR type, we don't have to do anything + if (cr->resrec.AnonInfo || cr->resrec.rrtype != kDNSType_PTR) + return; + + nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, cr->resrec.name); + if (nsec3CR) + { + cr->resrec.AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec); + if (cr->resrec.AnonInfo) + { + debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)", + RRDisplayString(m, cr->resrec.AnonInfo->nsec3RR), cr->resrec.name->c, + DNSTypeName(cr->resrec.rrtype)); + } + ReleaseCacheRecord(m, nsec3CR); + } +} + +mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2) +{ + // if a1 is NULL and a2 is not NULL AND vice-versa + // return false as there is a change. + if ((a1 != mDNSNULL) != (a2 != mDNSNULL)) + return mDNSfalse; + + // Both could be NULL or non-NULL + if (a1 && a2) + { + // The caller already verified that the owner name is the same. + // Check whether the RData is same. + if (!IdenticalSameNameRecord(a1->nsec3RR, a2->nsec3RR)) + { + debugf("IdenticalAnonInfo: nsec3RR mismatch"); + return mDNSfalse; + } + } + return mDNStrue; +} + +mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom) +{ + AnonymousInfo *aifrom = crfrom->resrec.AnonInfo; + AnonymousInfo *aito = crto->resrec.AnonInfo; + + (void) m; + + if (!aifrom) + return; + + if (aito) + { + crto->resrec.AnonInfo = aifrom; + FreeAnonInfo(aito); + crfrom->resrec.AnonInfo = mDNSNULL; + } + else + { + FreeAnonInfo(aifrom); + crfrom->resrec.AnonInfo = mDNSNULL; + } +} + +#else // !ANONYMOUS_DISABLED + +mDNSexport void ReInitAnonInfo(AnonymousInfo **si, const domainname *name) +{ + (void)si; + (void)name; +} + +mDNSexport AnonymousInfo * AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr) +{ + (void)service; + (void)AnonData; + (void)len; + (void)rr; + + return mDNSNULL; +} + +mDNSexport void FreeAnonInfo(AnonymousInfo *ai) +{ + (void)ai; +} + +mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion) +{ + (void)q; + (void)rr; + (void)ForQuestion; +} + +mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + (void)rr; + (void)q; + + return mDNSfalse; +} + +mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q) +{ + (void)m; + (void)McastNSEC3Records; + (void)q; +} + +mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr) +{ + (void)m; + (void)McastNSEC3Records; + (void)cr; +} + +mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom) +{ + (void)m; + (void)crto; + (void)crfrom; +} + +mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2) +{ + (void)a1; + (void)a2; + + return mDNStrue; +} + +#endif // !ANONYMOUS_DISABLED diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/anonymous.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/anonymous.h new file mode 100644 index 0000000000..2f2b4f8c4e --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/anonymous.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANONYMOUS_H_ +#define __ANONYMOUS_H_ + +extern void ReInitAnonInfo(AnonymousInfo **si, const domainname *name); +extern AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr); +extern void FreeAnonInfo(AnonymousInfo *ai); +extern void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion); +extern int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); +extern void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr); +extern void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q); +extern void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom); +extern mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2); + +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/dnssec.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/dnssec.h new file mode 100644 index 0000000000..1a8d95351b --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/dnssec.h @@ -0,0 +1,157 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __DNSSEC_H +#define __DNSSEC_H + +#include "CryptoAlg.h" +#include "mDNSDebug.h" + +typedef enum +{ + RRVS_rr, RRVS_rrsig, RRVS_key, RRVS_rrsig_key, RRVS_ds, RRVS_done, +} RRVerifierSet; + +typedef struct RRVerifier_struct RRVerifier; +typedef struct DNSSECVerifier_struct DNSSECVerifier; +typedef struct AuthChain_struct AuthChain; +typedef struct InsecureContext_struct InsecureContext; + +struct RRVerifier_struct +{ + RRVerifier *next; + mDNSu16 rrtype; + mDNSu16 rrclass; + mDNSu32 rroriginalttl; + mDNSu16 rdlength; + mDNSu16 found; + mDNSu32 namehash; + mDNSu32 rdatahash; + domainname name; + mDNSu8 *rdata; +}; + +// Each AuthChain element has one rrset (with multiple resource records of same type), rrsig and key +// that validates the rrset. +struct AuthChain_struct +{ + AuthChain *next; // Next element in the chain + RRVerifier *rrset; // RRSET that is authenticated + RRVerifier *rrsig; // Signature for that RRSET + RRVerifier *key; // Public key for that RRSET +}; + +#define ResetAuthChain(dv) { \ + (dv)->ac = mDNSNULL; \ + (dv)->actail = &((dv)->ac); \ +} + +typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); +// +// When we do a validation for a question, there might be additional validations that needs to be done e.g., +// wildcard expanded answer. It is also possible that in the case of nsec we need to prove both that a wildcard +// does not apply and the closest encloser proves that name does not exist. We identify these with the following +// flags. +// +// Note: In the following, by "marking the validation", we mean that as part of validation we need to prove +// the ones that are marked with. +// +// A wildcard may be used to answer a question. In that case, we need to verify that the right wildcard was +// used in answering the question. This is done by marking the validation with WILDCARD_PROVES_ANSWER_EXPANDED. +// +// Sometimes we get a NXDOMAIN response. In this case, we may have a wildcard where we need to prove +// that the wildcard proves that the name does not exist. This is done by marking the validation with +// WILDCARD_PROVES_NONAME_EXISTS. +// +// In the case of NODATA error, sometimes the name may exist but the query type does not exist. This is done by +// marking the validation with NSEC_PROVES_NOTYPE_EXISTS. +// +// In both NXDOMAIN and NODATA proofs, we may have to prove that the NAME does not exist. This is done by marking +// the validation with NSEC_PROVES_NONAME_EXISTS. +// +#define WILDCARD_PROVES_ANSWER_EXPANDED 0x00000001 +#define WILDCARD_PROVES_NONAME_EXISTS 0x00000002 +#define NSEC_PROVES_NOTYPE_EXISTS 0x00000004 +#define NSEC_PROVES_NONAME_EXISTS 0x00000008 +#define NSEC3_OPT_OUT 0x00000010 // OptOut was set in NSEC3 + +struct DNSSECVerifier_struct +{ + domainname origName; // Original question name that needs verification + mDNSu16 origType; // Original question type corresponding to origName + mDNSu16 currQtype; // Current question type that is being verified + mDNSInterfaceID InterfaceID; // InterfaceID of the question + DNSQuestion q; + mDNSu8 recursed; // Number of times recursed during validation + mDNSu8 ValidationRequired; // Copy of the question's ValidationRequired status + mDNSu8 InsecureProofDone; + mDNSu8 NumPackets; // Number of packets that we send on the wire for DNSSEC verification. + mDNSs32 StartTime; // Time the DNSSEC verification starts + mDNSu32 flags; + RRVerifierSet next; + domainname *wildcardName; // set if the answer is wildcard expanded + RRVerifier *pendingNSEC; + DNSSECVerifierCallback *DVCallback; + DNSSECVerifier *parent; + RRVerifier *rrset; // rrset for which we have to verify + RRVerifier *rrsig; // RRSIG for rrset + RRVerifier *key; // DNSKEY for rrset + RRVerifier *rrsigKey; // RRSIG for DNSKEY + RRVerifier *ds; // DS for DNSKEY set in parent zone + AuthChain *saveac; + AuthChain *ac; + AuthChain **actail; + AlgContext *ctx; +}; + + +struct InsecureContext_struct +{ + DNSSECVerifier *dv; // dv for which we are doing the insecure proof + mDNSu8 skip; // labels to skip for forming the name from origName + DNSSECStatus status; // status to deliver when done + mDNSu8 triggerLabelCount; // Label count of the name that triggered the insecure proof + DNSQuestion q; +}; + +#define LogDNSSEC LogOperation + +#define DNS_SERIAL_GT(a, b) ((int)((a) - (b)) > 0) +#define DNS_SERIAL_LT(a, b) ((int)((a) - (b)) < 0) + +extern void StartDNSSECVerification(mDNS *const m, void *context); +extern RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status); +extern mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set); +extern void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q); +extern void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv); +extern DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID, + mDNSu8 ValidationRequired, DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback); +extern void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, + mDNSu16 qtype, mDNSQuestionCallback *callback, void *context); +extern void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr); +extern void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae); +extern mStatus DNSNameToLowerCase(domainname *d, domainname *result); +extern int DNSMemCmp(const mDNSu8 *const m1, const mDNSu8 *const m2, int len); +extern int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain); +extern void ProveInsecure(mDNS *const m, DNSSECVerifier *dv, InsecureContext *ic, domainname *trigger); +extern void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value); +extern char *DNSSECStatusName(DNSSECStatus status); + +// DNSSECProbe belongs in DNSSECSupport.h but then we don't want to expose yet another plaform specific dnssec file +// to other platforms where dnssec is not supported. +extern void DNSSECProbe(mDNS *const m); + +#endif // __DNSSEC_H diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c index 2708aa0cac..2f9227eabd 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c @@ -1,13 +1,13 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 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. @@ -16,1783 +16,81 @@ * * 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 + * header files. It should compile on *any* platform that has a C compiler, without * making *any* assumptions about availability of so-called "standard" C functions, * routines, or types (which may or may not be present on any given platform). + */ - * Formatting notes: - * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion - * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>, - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) - - 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 +#include "DNSCommon.h" // Defines general DNS utility routines +#include "uDNS.h" // Defines entry points into unicast-specific routines +#include "nsec.h" +#include "dnssec.h" +#include "anonymous.h" -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 +// 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 -Revision 1.63 2002/09/21 20:44:49 zarzycki -Added APSL info +#include "dns_sd.h" // for kDNSServiceFlags* definitions -Revision 1.62 2002/09/20 03:25:37 cheshire -Fix some compiler warnings +#if APPLE_OSX_mDNSResponder -Revision 1.61 2002/09/20 01:05:24 cheshire -Don't kill the Extras list in mDNS_DeregisterService() +#include <WebFilterDNS/WebFilterDNS.h> -Revision 1.60 2002/09/19 23:47:35 cheshire -Added mDNS_RegisterNoSuchService() function for assertion of non-existence -of a particular named service +#if !NO_WCF +WCFConnection *WCFConnectionNew(void) __attribute__((weak_import)); +void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); -Revision 1.59 2002/09/19 21:25:34 cheshire -mDNS_snprintf() doesn't need to be in a separate file +// Do we really need to define a macro for "if"? +#define CHECK_WCF_FUNCTION(X) if (X) +#endif // ! NO_WCF -Revision 1.58 2002/09/19 04:20:43 cheshire -Remove high-ascii characters that confuse some systems +#else -Revision 1.57 2002/09/17 01:07:08 cheshire -Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() +#define NO_WCF 1 +#endif // APPLE_OSX_mDNSResponder -Revision 1.56 2002/09/16 19:44:17 cheshire -Merge in license terms from Quinn's copy, in preparation for Darwin release -*/ +// Forward declarations +mDNSlocal void BeginSleepProcessing(mDNS *const m); +mDNSlocal void RetrySPSRegistrations(mDNS *const m); +mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password); +mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); +mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); +mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q); +mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q); +mDNSlocal void mDNS_SendKeepalives(mDNS *const m); +mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, + mDNSu32 *seq, mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win); -#pragma ident "%Z%%M% %I% %E% SMI" +mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m); +mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m); +mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords); +mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, + const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records); -#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 } }; +// To Turn OFF mDNS_Tracer set MDNS_TRACER to 0 or undef it +#define MDNS_TRACER 1 + +#define NO_HINFO 1 // Any records bigger than this are considered 'large' records #define SmallRecordLimit 1024 @@ -1800,14 +98,29 @@ mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNS #define kMaxUpdateCredits 10 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6) +// define special NR_AnswerTo values +#define NR_AnswerMulticast (mDNSu8*)~0 +#define NR_AnswerUnicast (mDNSu8*)~1 + +// Defined to set the kDNSQClass_UnicastResponse bit in the first four query packets. +// else, it's just set it the first query. +#define mDNS_REQUEST_UNICAST_RESPONSE 0 + +// The code (see SendQueries() and BuildQuestion()) needs to have the +// RequestUnicast value set to a value one greater than the number of times you want the query +// sent with the "request unicast response" (QU) bit set. +#define SET_QU_IN_FIRST_QUERY 2 +#define SET_QU_IN_FIRST_FOUR_QUERIES 5 + + 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 - }; +{ + "b._dns-sd._udp.", // Browse + "db._dns-sd._udp.", // Default Browse + "lb._dns-sd._udp.", // Automatic Browse + "r._dns-sd._udp.", // Registration + "dr._dns-sd._udp." // Default Registration +}; #ifdef UNICAST_DISABLED #define uDNS_IsActiveQuery(q, u) mDNSfalse @@ -1816,458 +129,527 @@ mDNSexport const char *const mDNS_DomainTypeNames[] = // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - -#pragma mark - Specialized mDNS version of vsnprintf +#pragma mark - General Utility Functions #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); +// Returns true if this is a unique, authoritative LocalOnly record that answers questions of type +// A, AAAA , CNAME, or PTR. The caller should answer the question with this record and not send out +// the question on the wire if LocalOnlyRecordAnswersQuestion() also returns true. +// Main use is to handle /etc/hosts records and the LocalOnly PTR records created for localhost. +#define UniqueLocalOnlyRecord(rr) ((rr)->ARType == AuthRecordLocalOnly && \ + (rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \ + ((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \ + (rr)->resrec.rrtype == kDNSType_CNAME || \ + (rr)->resrec.rrtype == kDNSType_PTR)) + +mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q) +{ + mDNS_CheckLock(m); + +#if ForceAlerts + if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; +#endif - 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 (m->NextScheduledStopTime - q->StopTime > 0) + m->NextScheduledStopTime = q->StopTime; +} -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - General Utility Functions +mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) +{ + mDNS_CheckLock(m); + +#if ForceAlerts + if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; #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); - } + if (ActiveQuestion(q)) + { + // Depending on whether this is a multicast or unicast question we want to set either: + // m->NextScheduledQuery = NextQSendTime(q) or + // m->NextuDNSEvent = NextQSendTime(q) + mDNSs32 *const timer = mDNSOpaque16IsZero(q->TargetQID) ? &m->NextScheduledQuery : &m->NextuDNSEvent; + if (*timer - NextQSendTime(q) > 0) + *timer = NextQSendTime(q); + } +} + +mDNSlocal void ReleaseAuthEntity(AuthHash *r, AuthEntity *e) +{ +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + unsigned int i; + for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF; +#endif + e->next = r->rrauth_free; + r->rrauth_free = e; + r->rrauth_totalused--; +} + +mDNSlocal void ReleaseAuthGroup(AuthHash *r, AuthGroup **cp) +{ + AuthEntity *e = (AuthEntity *)(*cp); + LogMsg("ReleaseAuthGroup: Releasing AuthGroup %##s", (*cp)->name->c); + if ((*cp)->rrauth_tail != &(*cp)->members) + LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrauth_tail != &(*cp)->members)"); + if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); + (*cp)->name = mDNSNULL; + *cp = (*cp)->next; // Cut record from list + ReleaseAuthEntity(r, e); +} + +mDNSlocal AuthEntity *GetAuthEntity(AuthHash *r, const AuthGroup *const PreserveAG) +{ + AuthEntity *e = mDNSNULL; + + if (r->rrauth_lock) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } + r->rrauth_lock = 1; + + if (!r->rrauth_free) + { + // We allocate just one AuthEntity at a time because we need to be able + // free them all individually which normally happens when we parse /etc/hosts into + // AuthHash where we add the "new" entries and discard (free) the already added + // entries. If we allocate as chunks, we can't free them individually. + AuthEntity *storage = mDNSPlatformMemAllocate(sizeof(AuthEntity)); + storage->next = mDNSNULL; + r->rrauth_free = storage; + } + + // If we still have no free records, recycle all the records we can. + // Enumerating the entire auth is moderately expensive, so when we do it, we reclaim all the records we can in one pass. + if (!r->rrauth_free) + { + mDNSu32 oldtotalused = r->rrauth_totalused; + mDNSu32 slot; + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + { + AuthGroup **cp = &r->rrauth_hash[slot]; + while (*cp) + { + if ((*cp)->members || (*cp)==PreserveAG) cp=&(*cp)->next; + else ReleaseAuthGroup(r, cp); + } + } + LogInfo("GetAuthEntity: Recycled %d records to reduce auth cache from %d to %d", + oldtotalused - r->rrauth_totalused, oldtotalused, r->rrauth_totalused); + } + + if (r->rrauth_free) // If there are records in the free list, take one + { + e = r->rrauth_free; + r->rrauth_free = e->next; + if (++r->rrauth_totalused >= r->rrauth_report) + { + LogInfo("RR Auth now using %ld objects", r->rrauth_totalused); + if (r->rrauth_report < 100) r->rrauth_report += 10; + else if (r->rrauth_report < 1000) r->rrauth_report += 100; + else r->rrauth_report += 1000; + } + mDNSPlatformMemZero(e, sizeof(*e)); + } + + r->rrauth_lock = 0; + + return(e); +} + +mDNSexport AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name) +{ + AuthGroup *ag; + for (ag = r->rrauth_hash[slot]; ag; ag=ag->next) + if (ag->namehash == namehash && SameDomainName(ag->name, name)) + break; + return(ag); +} + +mDNSexport AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr) +{ + return(AuthGroupForName(r, slot, rr->namehash, rr->name)); +} + +mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr) +{ + mDNSu16 namelen = DomainNameLength(rr->name); + AuthGroup *ag = (AuthGroup*)GetAuthEntity(r, mDNSNULL); + if (!ag) { LogMsg("GetAuthGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } + ag->next = r->rrauth_hash[slot]; + ag->namehash = rr->namehash; + ag->members = mDNSNULL; + ag->rrauth_tail = &ag->members; + ag->NewLocalOnlyRecords = mDNSNULL; + if (namelen > sizeof(ag->namestorage)) + ag->name = mDNSPlatformMemAllocate(namelen); + else + ag->name = (domainname*)ag->namestorage; + if (!ag->name) + { + LogMsg("GetAuthGroup: Failed to allocate name storage for %##s", rr->name->c); + ReleaseAuthEntity(r, (AuthEntity*)ag); + return(mDNSNULL); + } + AssignDomainName(ag->name, rr->name); + + if (AuthGroupForRecord(r, slot, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c); + r->rrauth_hash[slot] = ag; + if (AuthGroupForRecord(r, slot, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c); + + return(ag); +} + +// Returns the AuthGroup in which the AuthRecord was inserted +mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr) +{ + AuthGroup *ag; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + ag = AuthGroupForRecord(r, slot, &rr->resrec); + if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec); // If we don't have a AuthGroup for this name, make one now + if (ag) + { + LogInfo("InsertAuthRecord: inserting auth record %s from table", ARDisplayString(m, rr)); + *(ag->rrauth_tail) = rr; // Append this record to tail of cache slot list + ag->rrauth_tail = &(rr->next); // Advance tail pointer + } + return ag; +} + +mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr) +{ + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(r, slot, &rr->resrec); + if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; } + rp = &(*ag)->members; + while (*rp) + { + if (*rp != rr) + rp=&(*rp)->next; + else + { + // We don't break here, so that we can set the tail below without tracking "prev" pointers + + LogInfo("RemoveAuthRecord: removing auth record %s from table", ARDisplayString(m, rr)); + *rp = (*rp)->next; // Cut record from list + } + } + // TBD: If there are no more members, release authgroup ? + (*ag)->rrauth_tail = rp; + return a; +} + +mDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name) +{ + CacheGroup *cg; + for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) + if (cg->namehash == namehash && SameDomainName(cg->name, name)) + break; + return(cg); +} mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) - { - return(CacheGroupForName(m, slot, rr->namehash, rr->name)); - } - -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; - } +{ + return(CacheGroupForName(m, slot, rr->namehash, rr->name)); +} + +mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself) +{ + NetworkInterfaceInfo *intf; + + if (addr->type == mDNSAddrType_IPv4) + { + // Normally we resist touching the NotAnInteger fields, but here we're doing tricky bitwise masking so we make an exception + if (mDNSv4AddressIsLinkLocal(&addr->ip.v4)) return(mDNStrue); + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) + if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) + { + if (myself) + { + if (mDNSSameIPv4Address(intf->ip.ip.v4, addr->ip.v4)) + *myself = mDNStrue; + else + *myself = mDNSfalse; + if (*myself) + debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning true", addr); + else + debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning false", addr); + } + return(mDNStrue); + } + } + + if (addr->type == mDNSAddrType_IPv6) + { + 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)) + { + if (myself) + { + if (mDNSSameIPv6Address(intf->ip.ip.v6, addr->ip.v6)) + *myself = mDNStrue; + else + *myself = mDNSfalse; + if (*myself) + debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning true", addr); + else + debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning false", addr); + } + return(mDNStrue); + } + } + + return(mDNSfalse); +} + +mDNSlocal NetworkInterfaceInfo *FirstInterfaceForID(mDNS *const m, const mDNSInterfaceID InterfaceID) +{ + NetworkInterfaceInfo *intf = m->HostInterfaces; + while (intf && intf->InterfaceID != InterfaceID) intf = intf->next; + return(intf); +} + +mDNSlocal NetworkInterfaceInfo *FirstIPv4LLInterfaceForID(mDNS *const m, const mDNSInterfaceID InterfaceID) +{ + NetworkInterfaceInfo *intf; + + if (!InterfaceID) + return mDNSNULL; + + // Note: We don't check for InterfaceActive, as the active interface could be IPv6 and + // we still want to find the first IPv4 Link-Local interface + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->InterfaceID == InterfaceID && + intf->ip.type == mDNSAddrType_IPv4 && mDNSv4AddressIsLinkLocal(&intf->ip.ip.v4)) + { + debugf("FirstIPv4LLInterfaceForID: found LL interface with address %.4a", &intf->ip.ip.v4); + return intf; + } + } + return (mDNSNULL); +} + +mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID) +{ + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + return(intf ? intf->ifname : mDNSNULL); +} + +// Caller should hold the lock +mDNSlocal void GenerateNegativeResponse(mDNS *const m, QC_result qc) +{ + DNSQuestion *q; + if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; } + q = m->CurrentQuestion; + LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL); + // We need to force the response through in the following cases + // + // a) SuppressUnusable questions that are suppressed + // b) Append search domains and retry the question + // + // The question may not have set Intermediates in which case we don't deliver negative responses. So, to force + // through we use "QC_forceresponse". + AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, qc); + if (m->CurrentQuestion == q) { q->ThisQInterval = 0; } // Deactivate this question + // Don't touch the question after this + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it +} + +mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr) +{ + const mDNSBool selfref = SameDomainName(&q->qname, &rr->rdata->u.name); + if (q->CNAMEReferrals >= 10 || selfref) + { + LogMsg("AnswerQuestionByFollowingCNAME: %p %##s (%s) NOT following CNAME referral %d%s for %s", + q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr)); + } + else + { + const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value + UDPSocket *sock = q->LocalSocket; + mDNSOpaque16 id = q->TargetQID; + + // if there is a message waiting at the socket, we want to process that instead + // of throwing it away. If we have a CNAME response that answers + // both A and AAAA question and while answering it we don't want to throw + // away the response where the actual addresses are present. + if (mDNSPlatformPeekUDP(m, q->LocalSocket)) + { + LogInfo("AnswerQuestionByFollowingCNAME: Preserving UDP socket for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->LocalSocket = mDNSNULL; + } + else + { + sock = mDNSNULL; + } + + // The SameDomainName check above is to ignore bogus CNAME records that point right back at + // themselves. Without that check we can get into a case where we have two duplicate questions, + // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals + // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because + // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates + // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals + // for either of them. This is not a problem for CNAME loops of two or more records because in + // those cases the newly re-appended question A has a different target name and therefore cannot be + // a duplicate of any other question ('B') which was itself a duplicate of the previous question A. + + // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect, + // and track CNAMEs coming and going, we should really create a subordinate query here, + // which we would subsequently cancel and retract if the CNAME referral record were removed. + // In reality this is such a corner case we'll ignore it until someone actually needs it. + + LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s", + q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr)); + + mDNS_StopQuery_internal(m, q); // Stop old query + AssignDomainName(&q->qname, &rr->rdata->u.name); // Update qname + q->qnamehash = DomainNameHashValue(&q->qname); // and namehash + // If a unicast query results in a CNAME that points to a .local, we need to re-try + // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal + // to try this as unicast query even though it is a .local name + if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname)) + { + LogInfo("AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p %##s (%s) Record %s", + q, q->qname.c, DNSTypeName(q->qtype), RRDisplayString(m, rr)); + q->InterfaceID = mDNSInterface_Unicast; + } + mDNS_StartQuery_internal(m, q); // start new query + // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal, + // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero + q->CNAMEReferrals = c; + if (sock) + { + // We have a message waiting and that should answer this question. + if (q->LocalSocket) + mDNSPlatformUDPClose(q->LocalSocket); + q->LocalSocket = sock; + q->TargetQID = id; + } + } +} + +// For a single given DNSQuestion pointed to by CurrentQuestion, deliver an add/remove result for the single given AuthRecord +// Note: All the callers should use the m->CurrentQuestion to see if the question is still valid or not +mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) +{ + DNSQuestion *q = m->CurrentQuestion; + mDNSBool followcname; + + if (!q) + { + LogMsg("AnswerLocalQuestionWithLocalAuthRecord: ERROR!! CurrentQuestion NULL while answering with %s", ARDisplayString(m, rr)); + return; + } + + followcname = FollowCNAME(q, &rr->resrec, AddRecord); + + // We should not be delivering results for record types Unregistered, Deregistering, and (unverified) Unique + if (!(rr->resrec.RecordType & kDNSRecordTypeActiveMask)) + { + LogMsg("AnswerLocalQuestionWithLocalAuthRecord: *NOT* delivering %s event for local record type %X %s", + AddRecord ? "Add" : "Rmv", rr->resrec.RecordType, ARDisplayString(m, rr)); + return; + } + + // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it + if (AddRecord) rr->AnsweredLocalQ = mDNStrue; + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + if (q->QuestionCallback && !q->NoAnswer) + { + q->CurrentAnswers += AddRecord ? 1 : -1; + if (UniqueLocalOnlyRecord(rr)) + { + if (!followcname || q->ReturnIntermed) + { + // Don't send this packet on the wire as we answered from /etc/hosts + q->ThisQInterval = 0; + q->LOAddressAnswers += AddRecord ? 1 : -1; + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + } + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + // The callback above could have caused the question to stop. Detect that + // using m->CurrentQuestion + if (followcname && m->CurrentQuestion == q) + AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); + return; + } + else + { + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + } + } + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again +} + +mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) +{ + if (m->CurrentQuestion) + LogMsg("AnswerInterfaceAnyQuestionsWithLocalAuthRecord: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + mDNSBool answered; + DNSQuestion *q = m->CurrentQuestion; + if (RRAny(rr)) + answered = ResourceRecordAnswersQuestion(&rr->resrec, q); + else + answered = LocalOnlyRecordAnswersQuestion(rr, q); + if (answered) + AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} + +// When a new local AuthRecord is created or deleted, AnswerAllLocalQuestionsWithLocalAuthRecord() +// delivers the appropriate add/remove events to listening questions: +// 1. It runs though all our LocalOnlyQuestions delivering answers as appropriate, +// stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion(). +// 2. If the AuthRecord is marked mDNSInterface_LocalOnly or mDNSInterface_P2P, then it also runs though +// our main question list, delivering answers to mDNSInterface_Any questions as appropriate, +// stopping if it reaches a NewQuestion -- brand-new questions are handled by AnswerNewQuestion(). +// +// AnswerAllLocalQuestionsWithLocalAuthRecord is used by the m->NewLocalRecords loop in mDNS_Execute(), +// and by mDNS_Deregister_internal() + +mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) +{ + if (m->CurrentQuestion) + LogMsg("AnswerAllLocalQuestionsWithLocalAuthRecord ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + m->CurrentQuestion = m->LocalOnlyQuestions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions) + { + mDNSBool answered; + DNSQuestion *q = m->CurrentQuestion; + // We are called with both LocalOnly/P2P record or a regular AuthRecord + if (RRAny(rr)) + answered = ResourceRecordAnswersQuestion(&rr->resrec, q); + else + answered = LocalOnlyRecordAnswersQuestion(rr, q); + if (answered) + AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + + m->CurrentQuestion = mDNSNULL; + + // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions + if (rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P) + AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, rr, AddRecord); + +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -2277,19 +659,32 @@ mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddR #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 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))) + (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) +// See RFC 6762: "8.3 Announcing" +// "The Multicast DNS responder MUST send at least two unsolicited responses, one second apart." +// Send 4, which is really 8 since we send on both IPv4 and IPv6. +#define InitialAnnounceCount ((mDNSu8)4) + +// For goodbye packets we set the count to 3, and for wakeups we set it to 18 +// (which will be up to 15 wakeup attempts over the course of 30 seconds, +// and then if the machine fails to wake, 3 goodbye packets). +#define GoodbyeCount ((mDNSu8)3) +#define WakeupCount ((mDNSu8)18) +#define MAX_PROBE_RESTARTS ((mDNSu8)20) + +// Number of wakeups we send if WakeOnResolve is set in the question +#define InitialWakeOnResolveCount ((mDNSu8)3) // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not. // This means that because the announce interval is doubled after sending the first packet, the first @@ -2299,15 +694,27 @@ mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddR #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2) #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2) -#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \ - (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \ - (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0) +#define DefaultAPIntervalForRecordType(X) ((X) &kDNSRecordTypeActiveSharedMask ? DefaultAnnounceIntervalForTypeShared : \ + (X) &kDNSRecordTypeUnique ? DefaultProbeIntervalForTypeUnique : \ + (X) &kDNSRecordTypeActiveUniqueMask ? DefaultAnnounceIntervalForTypeUnique : 0) #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0) #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR)) #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond) #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR)) +// Adjustment factor to avoid race condition (used for unicast cache entries) : +// Suppose real record has TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100. +// If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another +// 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox. +// To avoid this, we extend the record's effective TTL to give it a little extra grace period. +// We adjust the 100 second TTL to 127. This means that when we do our 80% query at 102 seconds, +// the cached copy at our local caching server will already have expired, so the server will be forced +// to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds. + +#define RRAdjustTTL(ttl) ((ttl) + ((ttl)/4) + 2) +#define RRUnadjustedTTL(ttl) ((((ttl) - 2) * 4) / 5) + #define MaxUnansweredQueries 4 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent @@ -2316,51 +723,48 @@ mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddR // 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)); - } + +// SameResourceRecordNameClassInterface is functionally the same as SameResourceRecordSignature, except rrtype does not have to match + +#define SameResourceRecordSignature(A,B) (A)->resrec.rrtype == (B)->resrec.rrtype && SameResourceRecordNameClassInterface((A),(B)) + +mDNSlocal mDNSBool SameResourceRecordNameClassInterface(const AuthRecord *const r1, const AuthRecord *const r2) +{ + if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); } + if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); } + if (r1->resrec.InterfaceID && + r2->resrec.InterfaceID && + r1->resrec.InterfaceID != r2->resrec.InterfaceID) return(mDNSfalse); + return (mDNSBool)( + r1->resrec.rrclass == r2->resrec.rrclass && + r1->resrec.namehash == r2->resrec.namehash && + SameDomainName(r1->resrec.name, r2->resrec.name)); +} // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict. // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY, // so a response of any type should match, even if it is not actually the type the client plans to use. + +// For now, to make it easier to avoid false conflicts, we treat SPS Proxy records like shared records, +// and require the rrtypes to match for the rdata to be considered potentially conflicting mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr) - { - if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } - if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); } - if (pktrr->resrec.InterfaceID && - authrr->resrec.InterfaceID && - pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); - if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && 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. +{ + if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } + if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); } + if (pktrr->resrec.InterfaceID && + authrr->resrec.InterfaceID && + pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); + if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) || authrr->WakeUp.HMAC.l[0]) + if (pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); + return (mDNSBool)( + pktrr->resrec.rrclass == authrr->resrec.rrclass && + pktrr->resrec.namehash == authrr->resrec.namehash && + SameDomainName(pktrr->resrec.name, authrr->resrec.name)); +} + +// CacheRecord *ka is the CacheRecord from the known answer list in the query. // This is the information that the requester believes to be correct. // AuthRecord *rr is the answer we are proposing to give, if not suppressed. // This is the information that we believe to be correct. @@ -2368,651 +772,1542 @@ mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const // (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); - } +{ + // 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); - } - } +{ + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + if ((rr->LastAPTime + rr->ThisAPInterval) - m->timenow > mDNSPlatformOneSecond * 10) + { + LogMsg("SetNextAnnounceProbeTime: ProbeCount %d Next in %d %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); + LogMsg("SetNextAnnounceProbeTime: m->SuppressProbes %d m->timenow %d diff %d", m->SuppressProbes, m->timenow, m->SuppressProbes - m->timenow); + } + if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); + // Some defensive code: + // If (rr->LastAPTime + rr->ThisAPInterval) happens to be far in the past, we don't want to allow + // NextScheduledProbe to be set excessively in the past, because that can cause bad things to happen. + // See: <rdar://problem/7795434> mDNS: Sometimes advertising stops working and record interval is set to zero + if (m->NextScheduledProbe - m->timenow < 0) + m->NextScheduledProbe = m->timenow; + } + else if (rr->AnnounceCount && (ResourceRecordIsValidAnswer(rr) || rr->resrec.RecordType == kDNSRecordTypeDeregistering)) + { + if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); + } +} mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) - { - // 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) - +{ + // For reverse-mapping Sleep Proxy PTR records, probe interval is one second + rr->ThisAPInterval = rr->AddressProxy.type ? mDNSPlatformOneSecond : DefaultAPIntervalForRecordType(rr->resrec.RecordType); + + // * If this is a record type that's going to probe, then we use the m->SuppressProbes time. + // * Otherwise, if it's not going to probe, but m->SuppressProbes is set because we have other + // records that are going to probe, then we delay its first announcement so that it will + // go out synchronized with the first announcement for the other records that *are* probing. + // This is a minor performance tweak that helps keep groups of related records synchronized together. + // The addition of "interval / 2" is to make sure that, in the event that any of the probes are + // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. + // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, + // because they will meet the criterion of being at least half-way to their scheduled announcement time. + // * If it's not going to probe and m->SuppressProbes is not already set then we should announce immediately. + + if (rr->ProbeCount) + { + // If we have no probe suppression time set, or it is in the past, set it now + if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) + { + // To allow us to aggregate probes when a group of services are registered together, + // the first probe is delayed 1/4 second. This means the common-case behaviour is: + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); + + // If we already have a *probe* scheduled to go out sooner, then use that time to get better aggregation + if (m->SuppressProbes - m->NextScheduledProbe >= 0) + m->SuppressProbes = NonZeroTime(m->NextScheduledProbe); + if (m->SuppressProbes - m->timenow < 0) // Make sure we don't set m->SuppressProbes excessively in the past + m->SuppressProbes = m->timenow; + + // If we already have a *query* scheduled to go out sooner, then use that time to get better aggregation + if (m->SuppressProbes - m->NextScheduledQuery >= 0) + m->SuppressProbes = NonZeroTime(m->NextScheduledQuery); + if (m->SuppressProbes - m->timenow < 0) // Make sure we don't set m->SuppressProbes excessively in the past + m->SuppressProbes = m->timenow; + + // except... don't expect to be able to send before the m->SuppressSending timer fires + if (m->SuppressSending && m->SuppressProbes - m->SuppressSending < 0) + m->SuppressProbes = NonZeroTime(m->SuppressSending); + + if (m->SuppressProbes - m->timenow > mDNSPlatformOneSecond * 8) + { + LogMsg("InitializeLastAPTime ERROR m->SuppressProbes %d m->NextScheduledProbe %d m->NextScheduledQuery %d m->SuppressSending %d %d", + m->SuppressProbes - m->timenow, + m->NextScheduledProbe - m->timenow, + m->NextScheduledQuery - m->timenow, + m->SuppressSending, + m->SuppressSending - m->timenow); + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); + } + } + rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; + } + else if (m->SuppressProbes && m->SuppressProbes - m->timenow >= 0) + rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; + else + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + + // For reverse-mapping Sleep Proxy PTR records we don't want to start probing instantly -- we + // wait one second to give the client a chance to go to sleep, and then start our ARP/NDP probing. + // After three probes one second apart with no answer, we conclude the client is now sleeping + // and we can begin broadcasting our announcements to take over ownership of that IP address. + // If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk + // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address. + if (rr->AddressProxy.type) + rr->LastAPTime = m->timenow; + + // Set LastMCTime to now, to inhibit multicast responses + // (no need to send additional multicast responses when we're announcing anyway) + rr->LastMCTime = m->timenow; + rr->LastMCInterface = mDNSInterfaceMark; + + SetNextAnnounceProbeTime(m, rr); +} + +mDNSlocal const domainname *SetUnicastTargetToHostName(mDNS *const m, AuthRecord *rr) +{ + const domainname *target; + if (rr->AutoTarget) + { + // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other + // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate, + // with the port number in our advertised SRV record automatically tracking the external mapped port. + DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + if (!AuthInfo || !AuthInfo->AutoTunnel) rr->AutoTarget = Target_AutoHostAndNATMAP; + } + + target = GetServiceTarget(m, rr); + if (!target || target->c[0] == 0) + { + // defer registration until we've got a target + LogInfo("SetUnicastTargetToHostName No target for %s", ARDisplayString(m, rr)); + rr->state = regState_NoTarget; + return mDNSNULL; + } + else + { + LogInfo("SetUnicastTargetToHostName target %##s for resource record %s", target->c, ARDisplayString(m,rr)); + return target; + } +} + +// Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname +// Eventually we should unify this with GetServiceTarget() in uDNS.c mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) - { - domainname *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); - } - } +{ + domainname *const target = GetRRDomainNameTarget(&rr->resrec); + const domainname *newname = &m->MulticastHostname; + + if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype)); + + if (!(rr->ForceMCast || rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P || IsLocalDomain(&rr->namestorage))) + { + const domainname *const n = SetUnicastTargetToHostName(m, rr); + if (n) newname = n; + else { target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; } + } + + if (target && SameDomainName(target, newname)) + debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c); + + if (target && !SameDomainName(target, newname)) + { + AssignDomainName(target, newname); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Update rdlength, rdestimate, rdatahash + + // If we're in the middle of probing this record, we need to start again, + // because changing its rdata may change the outcome of the tie-breaker. + // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.) + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + + // If we've announced this record, we really should send a goodbye packet for the old rdata before + // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, + // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. + if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared) + debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + + rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; + rr->ProbeRestartCount = 0; + 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 +{ + if (rr->RecordCallback) + { + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + rr->Acknowledged = mDNStrue; + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + rr->RecordCallback(m, rr, mStatus_NoError); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } +} + +mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) +{ + // Make sure that we don't activate the SRV record and associated service records, if it is in + // NoTarget state. First time when a service is being instantiated, SRV record may be in NoTarget state. + // We should not activate any of the other reords (PTR, TXT) that are part of the service. When + // the target becomes available, the records will be reregistered. + if (rr->resrec.rrtype != kDNSType_SRV) + { + AuthRecord *srvRR = mDNSNULL; + if (rr->resrec.rrtype == kDNSType_PTR) + srvRR = rr->Additional1; + else if (rr->resrec.rrtype == kDNSType_TXT) + srvRR = rr->DependentOn; + if (srvRR) + { + if (srvRR->resrec.rrtype != kDNSType_SRV) + { + LogMsg("ActivateUnicastRegistration: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR)); + } + else + { + LogInfo("ActivateUnicastRegistration: Found Service Record %s in state %d for %##s (%s)", + ARDisplayString(m, srvRR), srvRR->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + rr->state = srvRR->state; + } + } + } + + if (rr->state == regState_NoTarget) + { + LogInfo("ActivateUnicastRegistration record %s in regState_NoTarget, not activating", ARDisplayString(m, rr)); + return; + } + // When we wake up from sleep, we call ActivateUnicastRegistration. It is possible that just before we went to sleep, + // the service/record was being deregistered. In that case, we should not try to register again. For the cases where + // the records are deregistered due to e.g., no target for the SRV record, we would have returned from above if it + // was already in NoTarget state. If it was in the process of deregistration but did not complete fully before we went + // to sleep, then it is okay to start in Pending state as we will go back to NoTarget state if we don't have a target. + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to DeregPending", ARDisplayString(m, rr), rr->state); + rr->state = regState_DeregPending; + } + else + { + LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to Pending", ARDisplayString(m, rr), rr->state); + rr->state = regState_Pending; + } + rr->ProbeCount = 0; + rr->ProbeRestartCount = 0; + rr->AnnounceCount = 0; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + rr->expire = 0; // Forget about all the leases, start fresh + rr->uselease = mDNStrue; + rr->updateid = zeroID; + rr->SRVChanged = mDNSfalse; + rr->updateError = mStatus_NoError; + // RestartRecordGetZoneData calls this function whenever a new interface gets registered with core. + // The records might already be registered with the server and hence could have NAT state. + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval); +} + +// Two records qualify to be local duplicates if: +// (a) the RecordTypes are the same, or +// (b) one is Unique and the other Verified +// (c) either is in the process of deregistering #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \ - ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified)) -#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; + ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified) || \ + ((A)->resrec.RecordType == kDNSRecordTypeDeregistering || (B)->resrec.RecordType == kDNSRecordTypeDeregistering)) - 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 +#define RecordIsLocalDuplicate(A,B) \ + ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(& (A)->resrec, & (B)->resrec)) + +mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr) +{ + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(r, slot, &rr->resrec); + if (!a) return mDNSNULL; + rp = &(*ag)->members; + while (*rp) + { + if (!RecordIsLocalDuplicate(*rp, rr)) + rp=&(*rp)->next; + else + { + if ((*rp)->resrec.RecordType == kDNSRecordTypeDeregistering) + { + (*rp)->AnnounceCount = 0; + rp=&(*rp)->next; + } + else return *rp; + } + } + return (mDNSNULL); +} + +mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr) +{ + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(r, slot, &rr->resrec); + if (!a) return mDNSfalse; + rp = &(*ag)->members; + while (*rp) + { + const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; + const AuthRecord *s2 = (*rp)->RRSet ? (*rp)->RRSet : *rp; + if (s1 != s2 && SameResourceRecordSignature((*rp), rr) && !IdenticalSameNameRecord(&(*rp)->resrec, &rr->resrec)) + return mDNStrue; + else + rp=&(*rp)->next; + } + return (mDNSfalse); +} + +// checks to see if "rr" is already present +mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr) +{ + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(r, slot, &rr->resrec); + if (!a) return mDNSNULL; + rp = &(*ag)->members; + while (*rp) + { + if (*rp != rr) + rp=&(*rp)->next; + else + { + return *rp; + } + } + return (mDNSNULL); +} + + +mDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) +{ + if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + { + m->AutoTargetServices--; + LogInfo("DecrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices); + if (!m->AutoTargetServices) + DeadvertiseAllInterfaceRecords(m); + } +} + +mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) +{ + if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + { + int count = m->AutoTargetServices; + + // Bump up before calling AdvertiseAllInterfaceRecords. AdvertiseInterface + // returns without doing anything if the count is zero. + m->AutoTargetServices++; + LogInfo("IncrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices); + if (!count) + AdvertiseAllInterfaceRecords(m); + } +} + +mDNSlocal void getKeepaliveRaddr(mDNS *const m, AuthRecord *rr, mDNSAddr *raddr) +{ + mDNSAddr laddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; + + if (mDNS_KeepaliveRecord(&rr->resrec)) + { + mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, raddr, ð, &seq, &ack, &lport, &rport, &win); + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(raddr) || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport)) + { + LogMsg("getKeepaliveRaddr: not a valid record %s for keepalive %#a:%d %#a:%d", ARDisplayString(m, rr), &laddr, lport.NotAnInteger, raddr, rport.NotAnInteger); + return; + } + } +} + +// Exported so uDNS.c can call this +mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) +{ + domainname *target = GetRRDomainNameTarget(&rr->resrec); + AuthRecord *r; + AuthRecord **p = &m->ResourceRecords; + AuthRecord **d = &m->DuplicateRecords; + + if ((mDNSs32)rr->resrec.rroriginalttl <= 0) + { LogMsg("mDNS_Register_internal: TTL %X should be 1 - 0x7FFFFFFF %s", rr->resrec.rroriginalttl, ARDisplayString(m, rr)); return(mStatus_BadParamErr); } + + if (!rr->resrec.RecordType) + { LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } + + if (m->ShutdownTime) + { LogMsg("mDNS_Register_internal: Shutting down, can't register %s", ARDisplayString(m, rr)); return(mStatus_ServiceNotRunning); } + + if (m->DivertMulticastAdvertisements && !AuthRecord_uDNS(rr)) + { + mDNSInterfaceID previousID = rr->resrec.InterfaceID; + if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P) + { + rr->resrec.InterfaceID = mDNSInterface_LocalOnly; + rr->ARType = AuthRecordLocalOnly; + } + if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) + { + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); + if (intf && !intf->Advertise) { rr->resrec.InterfaceID = mDNSInterface_LocalOnly; rr->ARType = AuthRecordLocalOnly; } + } + if (rr->resrec.InterfaceID != previousID) + LogInfo("mDNS_Register_internal: Diverting record to local-only %s", ARDisplayString(m, rr)); + } + + if (RRLocalOnly(rr)) + { + if (CheckAuthSameRecord(&m->rrauth, rr)) + { + LogMsg("mDNS_Register_internal: ERROR!! Tried to register LocalOnly AuthRecord %p %##s (%s) that's already in the list", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_AlreadyRegistered); + } + } + else + { + while (*p && *p != rr) p=&(*p)->next; + if (*p) + { + LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the list", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_AlreadyRegistered); + } + } + + while (*d && *d != rr) d=&(*d)->next; + if (*d) + { + LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the Duplicate list", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_AlreadyRegistered); + } + + if (rr->DependentOn) + { + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + rr->resrec.RecordType = kDNSRecordTypeVerified; + else + { + LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_Invalid); + } + if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique))) + { + LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType); + return(mStatus_Invalid); + } + } + + rr->next = mDNSNULL; + + // Field Group 1: The actual information pertaining to this resource record + // Set up by client prior to call + + // Field Group 2: Persistent metadata for Authoritative Records +// rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->Callback = already set in mDNS_SetupResourceRecord +// rr->Context = already set in mDNS_SetupResourceRecord +// rr->RecordType = already set in mDNS_SetupResourceRecord // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client - // Make sure target is not uninitialized data, or we may crash writing debugging log messages - if (rr->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() + // Make sure target is not uninitialized data, or we may crash writing debugging log messages + if (rr->AutoTarget && target) target->c[0] = 0; + + // Field Group 3: Transient state for Authoritative Records + rr->Acknowledged = mDNSfalse; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->ProbeRestartCount = 0; + rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; + rr->AnsweredLocalQ = mDNSfalse; + rr->IncludeInProbe = mDNSfalse; + rr->ImmedUnicast = mDNSfalse; + rr->SendNSECNow = mDNSNULL; + rr->ImmedAnswer = mDNSNULL; + rr->ImmedAdditional = mDNSNULL; + rr->SendRNow = mDNSNULL; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + if (!rr->AutoTarget) InitializeLastAPTime(m, rr); // rr->LastAPTime = Set for us in InitializeLastAPTime() // rr->LastMCTime = Set for us in InitializeLastAPTime() // rr->LastMCInterface = Set for us in InitializeLastAPTime() - rr->NewRData = mDNSNULL; - rr->newrdlength = 0; - rr->UpdateCallback = mDNSNULL; - rr->UpdateCredits = kMaxUpdateCredits; - rr->NextUpdateCredit = 0; - rr->UpdateBlocked = 0; + rr->NewRData = mDNSNULL; + rr->newrdlength = 0; + rr->UpdateCallback = mDNSNULL; + rr->UpdateCredits = kMaxUpdateCredits; + rr->NextUpdateCredit = 0; + rr->UpdateBlocked = 0; + + // For records we're holding as proxy (except reverse-mapping PTR records) two announcements is sufficient + if (rr->WakeUp.HMAC.l[0] && !rr->AddressProxy.type) rr->AnnounceCount = 2; + + // Field Group 4: Transient uDNS state for Authoritative Records + rr->state = regState_Zero; + rr->uselease = 0; + rr->expire = 0; + rr->Private = 0; + rr->updateid = zeroID; + rr->updateIntID = zeroOpaque64; + rr->zone = rr->resrec.name; + rr->nta = mDNSNULL; + rr->tcp = mDNSNULL; + rr->OrigRData = 0; + rr->OrigRDLen = 0; + rr->InFlightRData = 0; + rr->InFlightRDLen = 0; + rr->QueuedRData = 0; + rr->QueuedRDLen = 0; + //mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); + // We should be recording the actual internal port for this service record here. Once we initiate our NAT mapping + // request we'll subsequently overwrite srv.port with the allocated external NAT port -- potentially multiple + // times with different values if the external NAT port changes during the lifetime of the service registration. + //if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; // rr->resrec.interface = already set in mDNS_SetupResourceRecord -// rr->resrec.name->c = MUST be set by client +// rr->resrec.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; - } + // 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; } - // For records that are not going to probe, acknowledge them right away - if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) - AcknowledgeRecord(m, rr); + if (rr->AutoTarget) + { + SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime(); +#ifndef UNICAST_DISABLED + // If we have no target record yet, SetTargetToHostName will set rr->state == regState_NoTarget + // In this case we leave the record half-formed in the list, and later we'll remove it from the list and re-add it properly. + if (rr->state == regState_NoTarget) + { + // Initialize the target so that we don't crash while logging etc. + domainname *tar = GetRRDomainNameTarget(&rr->resrec); + if (tar) tar->c[0] = 0; + LogInfo("mDNS_Register_internal: record %s in NoTarget state", ARDisplayString(m, rr)); + } +#endif + } + else + { + rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); + rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue); + } + + if (!ValidateDomainName(rr->resrec.name)) + { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } + + // Don't do this until *after* we've set rr->resrec.rdlength + if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata)) + { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } + + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(&rr->resrec); + + if (RRLocalOnly(rr)) + { + // If this is supposed to be unique, make sure we don't have any name conflicts. + // If we found a conflict, we may still want to insert the record in the list but mark it appropriately + // (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more + // complications and not clear whether there are any benefits. See rdar:9304275 for details. + // Hence, just bail out. + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + { + if (CheckAuthRecordConflict(&m->rrauth, rr)) + { + LogInfo("mDNS_Register_internal: Name conflict %s (%p), InterfaceID %p", ARDisplayString(m, rr), rr, rr->resrec.InterfaceID); + return mStatus_NameConflict; + } + } + } + + // For uDNS records, we don't support duplicate checks at this time. +#ifndef UNICAST_DISABLED + if (AuthRecord_uDNS(rr)) + { + if (!m->NewLocalRecords) m->NewLocalRecords = rr; + // When we called SetTargetToHostName, it may have caused mDNS_Register_internal to be re-entered, appending new + // records to the list, so we now need to update p to advance to the new end to the list before appending our new record. + // Note that for AutoTunnel this should never happen, but this check makes the code future-proof. + while (*p) p=&(*p)->next; + *p = rr; + if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; + rr->ProbeCount = 0; + rr->ProbeRestartCount = 0; + rr->AnnounceCount = 0; + if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr); + return(mStatus_NoError); // <--- Note: For unicast records, code currently bails out at this point + } +#endif - return(mStatus_NoError); - } + // Now that we've finished building our new record, make sure it's not identical to one we already have + if (RRLocalOnly(rr)) + { + rr->ProbeCount = 0; + rr->ProbeRestartCount = 0; + rr->AnnounceCount = 0; + r = CheckAuthIdenticalRecord(&m->rrauth, rr); + } + else + { + for (r = m->ResourceRecords; r; r=r->next) + if (RecordIsLocalDuplicate(r, rr)) + { + if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0; + else break; + } + } + + if (r) + { + debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr)); + *d = rr; + // If the previous copy of this record is already verified unique, + // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. + // Setting ProbeCount to zero will cause SendQueries() to advance this record to + // kDNSRecordTypeVerified state and call the client callback at the next appropriate time. + if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified) + rr->ProbeCount = 0; + } + else + { + debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr)); + if (RRLocalOnly(rr)) + { + AuthGroup *ag; + ag = InsertAuthRecord(m, &m->rrauth, rr); + if (ag && !ag->NewLocalOnlyRecords) { + m->NewLocalOnlyRecords = mDNStrue; + ag->NewLocalOnlyRecords = rr; + } + // No probing for LocalOnly records, Acknowledge them right away + if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; + AcknowledgeRecord(m, rr); + return(mStatus_NoError); + } + else + { + if (!m->NewLocalRecords) m->NewLocalRecords = rr; + *p = rr; + } + } + + // If this is a keepalive record, fetch the MAC address of the remote host. + // This is used by the in-NIC proxy to send the keepalive packets. + if (mDNS_KeepaliveRecord(&rr->resrec)) + { + // Set the record type to known unique to prevent probing keep alive records. + // Also make sure we do not announce the keepalive records. + rr->resrec.RecordType = kDNSRecordTypeKnownUnique; + rr->AnnounceCount = 0; + mDNSAddr raddr; + getKeepaliveRaddr(m, rr, &raddr); + // This is an asynchronous call. Once the remote MAC address is available, helper will schedule an + // asynchronous task to update the resource record + mDNSPlatformGetRemoteMacAddr(m, &raddr); + } + + if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above + { + // We have inserted the record in the list. See if we have to advertise the A/AAAA,HINFO,PTR records. + IncrementAutoTargetServices(m, 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); + + // Adding a record may affect whether or not we should sleep + mDNS_UpdateAllowSleep(m); + } + + return(mStatus_NoError); +} mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) - { - m->ProbeFailTime = m->timenow; - m->NumFailedProbes++; - // If we've had fifteen or more probe failures, rate-limit to one every five seconds. - // If a bunch of hosts have all been configured with the same name, then they'll all - // conflict and run through the same series of names: name-2, name-3, name-4, etc., - // up to name-10. After that they'll start adding random increments in the range 1-100, - // so they're more likely to branch out in the available namespace and settle on a set of - // unique names quickly. If after five more tries the host is still conflicting, then we - // may have a serious problem, so we start rate-limiting so we don't melt down the network. - if (m->NumFailedProbes >= 15) - { - m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); - LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect", - m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - } - } +{ + 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. +{ + RData *OldRData = rr->resrec.rdata; + mDNSu16 OldRDLen = rr->resrec.rdlength; + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, OldRData, OldRDLen); // ... and let the client know +} + +// Note: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -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 +// Exported so uDNS.c can call this +mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt) +{ + AuthRecord *r2; + mDNSu8 RecordType = rr->resrec.RecordType; + AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records + mDNSBool dupList = mDNSfalse; + + if (RRLocalOnly(rr)) + { + AuthGroup *a; + AuthGroup **ag = &a; + AuthRecord **rp; + const mDNSu32 slot = AuthHashSlot(rr->resrec.name); + + a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); + if (!a) return mDNSfalse; + rp = &(*ag)->members; + while (*rp && *rp != rr) rp=&(*rp)->next; + p = rp; + } + else + { + while (*p && *p != rr) p=&(*p)->next; + } + + if (*p) + { + // We found our record on the main list. See if there are any duplicates that need special handling. + if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment + { + // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished + // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory. + for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF; + } + else + { + // Before we delete the record (and potentially send a goodbye packet) + // first see if we have a record on the duplicate list ready to take over from it. + AuthRecord **d = &m->DuplicateRecords; + while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next; + if (*d) + { + AuthRecord *dup = *d; + debugf("mDNS_Register_internal: Duplicate record %p taking over from %p %##s (%s)", + dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + *d = dup->next; // Cut replacement record from DuplicateRecords list + if (RRLocalOnly(rr)) + { + dup->next = mDNSNULL; + if (!InsertAuthRecord(m, &m->rrauth, dup)) LogMsg("mDNS_Deregister_internal: ERROR!! cannot insert %s", ARDisplayString(m, dup)); + } + else + { + dup->next = rr->next; // And then... + rr->next = dup; // ... splice it in right after the record we're about to delete + } + dup->resrec.RecordType = rr->resrec.RecordType; + dup->ProbeCount = rr->ProbeCount; + dup->ProbeRestartCount = rr->ProbeRestartCount; + dup->AnnounceCount = rr->AnnounceCount; + dup->RequireGoodbye = rr->RequireGoodbye; + dup->AnsweredLocalQ = rr->AnsweredLocalQ; + dup->ImmedAnswer = rr->ImmedAnswer; + dup->ImmedUnicast = rr->ImmedUnicast; + dup->ImmedAdditional = rr->ImmedAdditional; + dup->v4Requester = rr->v4Requester; + dup->v6Requester = rr->v6Requester; + dup->ThisAPInterval = rr->ThisAPInterval; + dup->LastAPTime = rr->LastAPTime; + dup->LastMCTime = rr->LastMCTime; + dup->LastMCInterface = rr->LastMCInterface; + dup->Private = rr->Private; + dup->state = rr->state; + rr->RequireGoodbye = mDNSfalse; + rr->AnsweredLocalQ = mDNSfalse; + } + } + } + else + { + // We didn't find our record on the main list; try the DuplicateRecords list instead. + p = &m->DuplicateRecords; + while (*p && *p != rr) p=&(*p)->next; + // If we found our record on the duplicate list, then make sure we don't send a goodbye for it + if (*p) + { + // Duplicate records are not used for sending wakeups or goodbyes. Hence, deregister them + // immediately. When there is a conflict, we deregister all the conflicting duplicate records + // also that have been marked above in this function. In that case, we come here and if we don't + // deregister (unilink from the DuplicateRecords list), we will be recursing infinitely. Hence, + // clear the HMAC which will cause it to deregister. See <rdar://problem/10380988> for + // details. + rr->WakeUp.HMAC = zeroEthAddr; + rr->RequireGoodbye = mDNSfalse; + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + dupList = mDNStrue; + } + if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + } + + if (!*p) + { + // No need to log an error message if we already know this is a potentially repeated deregistration + if (drt != mDNS_Dereg_repeat) + LogMsg("mDNS_Deregister_internal: Record %p not found in list %s", rr, ARDisplayString(m,rr)); + return(mStatus_BadReferenceErr); + } + + // If this is a shared record and we've announced it at least once, + // we need to retract that announcement before we delete the record + + // If this is a record (including mDNSInterface_LocalOnly records) for which we've given local-only answers then + // it's tempting to just do "AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse)" here, but that would not not be safe. + // The AnswerAllLocalQuestionsWithLocalAuthRecord routine walks the question list invoking client callbacks, using the "m->CurrentQuestion" + // mechanism to cope with the client callback modifying the question list while that's happening. + // However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain) + // which means that the "m->CurrentQuestion" mechanism is already in use to protect that list, so we can't use it twice. + // More generally, if we invoke callbacks from within a client callback, then those callbacks could deregister other + // records, thereby invoking yet more callbacks, without limit. + // The solution is to defer delivering the "Remove" events until mDNS_Execute time, just like we do for sending + // actual goodbye packets. #ifndef UNICAST_DISABLED - if (!(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 (AuthRecord_uDNS(rr)) + { + if (rr->RequireGoodbye) + { + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + m->LocalRemoveEvents = mDNStrue; + uDNS_DeregisterRecord(m, rr); + // At this point unconditionally we bail out + // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration, + // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration + // process and will complete asynchronously. Either way we don't need to do anything more here. + return(mStatus_NoError); + } + // Sometimes the records don't complete proper deregistration i.e., don't wait for a response + // from the server. In that case, if the records have been part of a group update, clear the + // state here. Some recors e.g., AutoTunnel gets reused without ever being completely initialized + rr->updateid = zeroID; + + // We defer cleaning up NAT state only after sending goodbyes. This is important because + // RecordRegistrationGotZoneData guards against creating NAT state if clientContext is non-NULL. + // This happens today when we turn on/off interface where we get multiple network transitions + // and RestartRecordGetZoneData triggers re-registration of the resource records even though + // they may be in Registered state which causes NAT information to be setup multiple times. Defering + // the cleanup here keeps clientContext non-NULL and hence prevents that. Note that cleaning up + // NAT state here takes care of the case where we did not send goodbyes at all. + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + } +#endif // UNICAST_DISABLED + + if (RecordType == kDNSRecordTypeUnregistered) + LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m, rr)); + else if (RecordType == kDNSRecordTypeDeregistering) + { + LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m, rr)); + return(mStatus_BadReferenceErr); + } + + // <rdar://problem/7457925> Local-only questions don't get remove events for unique records + // We may want to consider changing this code so that we generate local-only question "rmv" + // events (and maybe goodbye packets too) for unique records as well as for shared records + // Note: If we change the logic for this "if" statement, need to ensure that the code in + // CompleteDeregistration() sets the appropriate state variables to gaurantee that "else" + // clause will execute here and the record will be cut from the list. + if (rr->WakeUp.HMAC.l[0] || + (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->AnsweredLocalQ))) + { + verbosedebugf("mDNS_Deregister_internal: Starting deregistration for %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + rr->resrec.rroriginalttl = 0; + rr->AnnounceCount = rr->WakeUp.HMAC.l[0] ? WakeupCount : (drt == mDNS_Dereg_rapid) ? 1 : GoodbyeCount; + rr->ThisAPInterval = mDNSPlatformOneSecond * 2; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + m->LocalRemoveEvents = mDNStrue; + if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0) + m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10); + } + else + { + if (!dupList && RRLocalOnly(rr)) + { + AuthGroup *ag = RemoveAuthRecord(m, &m->rrauth, rr); + if (ag->NewLocalOnlyRecords == rr) ag->NewLocalOnlyRecords = rr->next; + } + else + { + *p = rr->next; // Cut this record from the list + if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next; + DecrementAutoTargetServices(m, rr); + } + // If someone is about to look at this, bump the pointer forward + if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; + rr->next = mDNSNULL; + + // Should we generate local remove events here? + // i.e. something like: + // if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; } + + verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeUnregistered; + + if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared) + debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + + // If we have an update queued up which never executed, give the client a chance to free that memory + if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + + + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + // In this case the likely client action to the mStatus_MemFree message is to free the memory, + // so any attempt to touch rr after this is likely to lead to a crash. + if (drt != mDNS_Dereg_conflict) + { + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + LogInfo("mDNS_Deregister_internal: mStatus_MemFree for %s", ARDisplayString(m, rr)); + if (rr->RecordCallback) + rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + else + { + RecordProbeFailure(m, rr); + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) + rr->RecordCallback(m, rr, mStatus_NameConflict); // MUST NOT touch rr after this + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously. + // Note that with all the client callbacks going on, by the time we get here all the + // records we marked may have been explicitly deregistered by the client anyway. + r2 = m->DuplicateRecords; + while (r2) + { + if (r2->ProbeCount != 0xFF) + { + r2 = r2->next; + } + else + { + mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); + // As this is a duplicate record, it will be unlinked from the list + // immediately + r2 = m->DuplicateRecords; + } + } + } + } + mDNS_UpdateAllowSleep(m); + 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)); - } +{ + 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); - } - } +{ + AuthRecord *rr, *rr2; + for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put + { + // (Note: This is an "if", not a "while". If we add a record, we'll find it again + // later in the "for" loop, and we will follow further "additional" links then.) + if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) + AddRecordToResponseList(nrpp, rr->Additional1, rr); + + if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) + AddRecordToResponseList(nrpp, rr->Additional2, rr); + + // For SRV records, automatically add the Address record(s) for the target host + if (rr->resrec.rrtype == kDNSType_SRV) + { + for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records + if (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ... + ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... + rr->resrec.rdatahash == rr2->resrec.namehash && // ... whose name is the name of the SRV target + SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name)) + AddRecordToResponseList(nrpp, rr2, rr); + } + else if (RRTypeIsAddressType(rr->resrec.rrtype)) // For A or AAAA, put counterpart as additional + { + for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records + if (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ... + ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... + rr->resrec.namehash == rr2->resrec.namehash && // ... and have the same name + SameDomainName(rr->resrec.name, rr2->resrec.name)) + AddRecordToResponseList(nrpp, rr2, rr); + } + else if (rr->resrec.rrtype == kDNSType_PTR) // For service PTR, see if we want to add DeviceInfo record + { + if (ResourceRecordIsValidInterfaceAnswer(&m->DeviceInfo, InterfaceID) && + SameDomainLabel(rr->resrec.rdata->u.name.c, m->DeviceInfo.resrec.name->c)) + AddRecordToResponseList(nrpp, &m->DeviceInfo, rr); + } + } +} + +mDNSlocal int AnonInfoSpace(AnonymousInfo *info) +{ + ResourceRecord *rr = info->nsec3RR; + + // 2 bytes for compressed name + type (2) class (2) TTL (4) rdlength (2) rdata (n) + return (2 + 10 + rr->rdlength); +} 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. +{ + AuthRecord *rr; + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + int AnoninfoSpace = 0; + + // Make a list of all our records that need to be unicast to this destination + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + // If we find we can no longer unicast this answer, clear ImmedUnicast + if (rr->ImmedAnswer == mDNSInterfaceMark || + mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) || + mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr) ) + rr->ImmedUnicast = mDNSfalse; + + if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID) + { + if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) || + (dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6))) + { + rr->ImmedAnswer = mDNSNULL; // Clear the state fields + rr->ImmedUnicast = mDNSfalse; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + + // Only sent records registered for P2P over P2P interfaces + if (intf && !mDNSPlatformValidRecordForInterface(rr, intf)) + { + LogInfo("SendDelayedUnicastResponse: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, InterfaceID)); + continue; + } + + if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo + { + rr->NR_AnswerTo = NR_AnswerMulticast; + *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.AnonInfo) + { + AnoninfoSpace += AnonInfoSpace(rr->resrec.AnonInfo); + rr->resrec.AnonInfo->SendNow = mDNSInterfaceMark; + } + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + + // Retract the limit by AnoninfoSpace which we need to put the AnoInfo option. + newptr = PutResourceRecordTTLWithLimit(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl, + m->omsg.data + (AllowedRRSpace(&m->omsg) - AnoninfoSpace)); + + 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; + } + + // We have reserved the space for AnonInfo option. PutResourceRecord uses the + // standard limit (AllowedRRSpace) and we should have space now. + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->resrec.AnonInfo && rr->resrec.AnonInfo->SendNow == mDNSInterfaceMark) + { + ResourceRecord *nsec3RR = rr->resrec.AnonInfo->nsec3RR; + + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAuthorities, nsec3RR); + if (newptr) + { + responseptr = newptr; + debugf("SendDelayedUnicastResponse: Added NSEC3 Record %s on %p", RRDisplayString(m, nsec3RR), intf->InterfaceID); + } + else + { + // We allocated space and we should not fail. Don't break, we need to clear the SendNow flag. + LogMsg("SendDelayedUnicastResponse: ERROR!! Cannot Add NSEC3 Record %s on %p", RRDisplayString(m, nsec3RR), intf->InterfaceID); + } + rr->resrec.AnonInfo->SendNow = mDNSNULL; + } + } + + // Add additionals, if there's space + while (ResponseRecords && !ResponseRecords->NR_AnswerTo) + { + rr = ResponseRecords; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + + if (newptr) responseptr = newptr; + if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue; + else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + } + + if (m->omsg.h.numAnswers) + mDNSSendDNSMessage(m, &m->omsg, responseptr, InterfaceID, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); + } +} + +// CompleteDeregistration guarantees that on exit the record will have been cut from the m->ResourceRecords list +// and the client's mStatus_MemFree callback will have been invoked +mDNSexport void CompleteDeregistration(mDNS *const m, AuthRecord *rr) +{ + LogInfo("CompleteDeregistration: called for Resource record %s", ARDisplayString(m, rr)); + // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() that + // it should go ahead and immediately dispose of this registration + rr->resrec.RecordType = kDNSRecordTypeShared; + rr->RequireGoodbye = mDNSfalse; + rr->WakeUp.HMAC = zeroEthAddr; + if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, QC_rmv); rr->AnsweredLocalQ = mDNSfalse; } + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this +} + +// DiscardDeregistrations is used on shutdown and sleep to discard (forcibly and immediately) +// any deregistering records that remain in the m->ResourceRecords list. +// DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, +// which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void DiscardDeregistrations(mDNS *const m) - { - if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set"); - 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 +{ + if (m->CurrentRecord) + LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (!AuthRecord_uDNS(rr) && rr->resrec.RecordType == kDNSRecordTypeDeregistering) + CompleteDeregistration(m, rr); // Don't touch rr after this + else + m->CurrentRecord = rr->next; + } +} + +mDNSlocal mStatus GetLabelDecimalValue(const mDNSu8 *const src, mDNSu8 *dst) +{ + int i, val = 0; + if (src[0] < 1 || src[0] > 3) return(mStatus_Invalid); + for (i=1; i<=src[0]; i++) + { + if (src[i] < '0' || src[i] > '9') return(mStatus_Invalid); + val = val * 10 + src[i] - '0'; + } + if (val > 255) return(mStatus_Invalid); + *dst = (mDNSu8)val; + return(mStatus_NoError); +} + +mDNSlocal mStatus GetIPv4FromName(mDNSAddr *const a, const domainname *const name) +{ + int skip = CountLabels(name) - 6; + if (skip < 0) { LogMsg("GetIPFromName: Need six labels in IPv4 reverse mapping name %##s", name); return mStatus_Invalid; } + if (GetLabelDecimalValue(SkipLeadingLabels(name, skip+3)->c, &a->ip.v4.b[0]) || + GetLabelDecimalValue(SkipLeadingLabels(name, skip+2)->c, &a->ip.v4.b[1]) || + GetLabelDecimalValue(SkipLeadingLabels(name, skip+1)->c, &a->ip.v4.b[2]) || + GetLabelDecimalValue(SkipLeadingLabels(name, skip+0)->c, &a->ip.v4.b[3])) return mStatus_Invalid; + a->type = mDNSAddrType_IPv4; + return(mStatus_NoError); +} + +#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ + ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ + ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1) + +mDNSlocal mStatus GetIPv6FromName(mDNSAddr *const a, const domainname *const name) +{ + int i, h, l; + const domainname *n; + + int skip = CountLabels(name) - 34; + if (skip < 0) { LogMsg("GetIPFromName: Need 34 labels in IPv6 reverse mapping name %##s", name); return mStatus_Invalid; } + + n = SkipLeadingLabels(name, skip); + for (i=0; i<16; i++) + { + if (n->c[0] != 1) return mStatus_Invalid; + l = HexVal(n->c[1]); + n = (const domainname *)(n->c + 2); + + if (n->c[0] != 1) return mStatus_Invalid; + h = HexVal(n->c[1]); + n = (const domainname *)(n->c + 2); + + if (l<0 || h<0) return mStatus_Invalid; + a->ip.v6.b[15-i] = (mDNSu8)((h << 4) | l); + } + + a->type = mDNSAddrType_IPv6; + return(mStatus_NoError); +} + +mDNSlocal mDNSs32 ReverseMapDomainType(const domainname *const name) +{ + int skip = CountLabels(name) - 2; + if (skip >= 0) + { + const domainname *suffix = SkipLeadingLabels(name, skip); + if (SameDomainName(suffix, (const domainname*)"\x7" "in-addr" "\x4" "arpa")) return mDNSAddrType_IPv4; + if (SameDomainName(suffix, (const domainname*)"\x3" "ip6" "\x4" "arpa")) return mDNSAddrType_IPv6; + } + return(mDNSAddrType_None); +} + +mDNSlocal void SendARP(mDNS *const m, const mDNSu8 op, const AuthRecord *const rr, + const mDNSv4Addr *const spa, const mDNSEthAddr *const tha, const mDNSv4Addr *const tpa, const mDNSEthAddr *const dst) +{ + int i; + mDNSu8 *ptr = m->omsg.data; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); + if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; } + + // 0x00 Destination address + for (i=0; i<6; i++) *ptr++ = dst->b[i]; + + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0]; + + // 0x0C ARP Ethertype (0x0806) + *ptr++ = 0x08; *ptr++ = 0x06; + + // 0x0E ARP header + *ptr++ = 0x00; *ptr++ = 0x01; // Hardware address space; Ethernet = 1 + *ptr++ = 0x08; *ptr++ = 0x00; // Protocol address space; IP = 0x0800 + *ptr++ = 6; // Hardware address length + *ptr++ = 4; // Protocol address length + *ptr++ = 0x00; *ptr++ = op; // opcode; Request = 1, Response = 2 + + // 0x16 Sender hardware address (our MAC address) + for (i=0; i<6; i++) *ptr++ = intf->MAC.b[i]; + + // 0x1C Sender protocol address + for (i=0; i<4; i++) *ptr++ = spa->b[i]; + + // 0x20 Target hardware address + for (i=0; i<6; i++) *ptr++ = tha->b[i]; + + // 0x26 Target protocol address + for (i=0; i<4; i++) *ptr++ = tpa->b[i]; + + // 0x2A Total ARP Packet length 42 bytes + mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); +} + +mDNSlocal mDNSu16 CheckSum(const void *const data, mDNSs32 length, mDNSu32 sum) +{ + const mDNSu16 *ptr = data; + while (length > 0) { length -= 2; sum += *ptr++; } + sum = (sum & 0xFFFF) + (sum >> 16); + sum = (sum & 0xFFFF) + (sum >> 16); + return(sum != 0xFFFF ? sum : 0); +} + +mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *const dst, const mDNSu8 protocol, const void *const data, const mDNSu32 length) +{ + IPv6PseudoHeader ph; + ph.src = *src; + ph.dst = *dst; + ph.len.b[0] = length >> 24; + ph.len.b[1] = length >> 16; + ph.len.b[2] = length >> 8; + ph.len.b[3] = length; + ph.pro.b[0] = 0; + ph.pro.b[1] = 0; + ph.pro.b[2] = 0; + ph.pro.b[3] = protocol; + return CheckSum(&ph, sizeof(ph), CheckSum(data, length, 0)); +} + +mDNSlocal void SendNDP(mDNS *const m, const mDNSu8 op, const mDNSu8 flags, const AuthRecord *const rr, + const mDNSv6Addr *const spa, const mDNSEthAddr *const tha, const mDNSv6Addr *const tpa, const mDNSEthAddr *const dst) +{ + int i; + mDNSOpaque16 checksum; + mDNSu8 *ptr = m->omsg.data; + // Some recipient hosts seem to ignore Neighbor Solicitations if the IPv6-layer destination address is not the + // appropriate IPv6 solicited node multicast address, so we use that IPv6-layer destination address, even though + // at the Ethernet-layer we unicast the packet to the intended target, to avoid wasting network bandwidth. + const mDNSv6Addr mc = { { 0xFF,0x02,0x00,0x00, 0,0,0,0, 0,0,0,1, 0xFF,tpa->b[0xD],tpa->b[0xE],tpa->b[0xF] } }; + const mDNSv6Addr *const v6dst = (op == NDP_Sol) ? &mc : tpa; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); + if (!intf) { LogMsg("SendNDP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; } + + // 0x00 Destination address + for (i=0; i<6; i++) *ptr++ = dst->b[i]; + // Right now we only send Neighbor Solicitations to verify whether the host we're proxying for has gone to sleep yet. + // Since we know who we're looking for, we send it via Ethernet-layer unicast, rather than bothering every host on the + // link with a pointless link-layer multicast. + // Should we want to send traditional Neighbor Solicitations in the future, where we really don't know in advance what + // Ethernet-layer address we're looking for, we'll need to send to the appropriate Ethernet-layer multicast address: + // *ptr++ = 0x33; + // *ptr++ = 0x33; + // *ptr++ = 0xFF; + // *ptr++ = tpa->b[0xD]; + // *ptr++ = tpa->b[0xE]; + // *ptr++ = tpa->b[0xF]; + + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) + if (tha) + *ptr++ = tha->b[i]; + else + *ptr++ = intf->MAC.b[i]; + + // 0x0C IPv6 Ethertype (0x86DD) + *ptr++ = 0x86; *ptr++ = 0xDD; + + // 0x0E IPv6 header + *ptr++ = 0x60; *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; // Version, Traffic Class, Flow Label + *ptr++ = 0x00; *ptr++ = 0x20; // Length + *ptr++ = 0x3A; // Protocol == ICMPv6 + *ptr++ = 0xFF; // Hop Limit + + // 0x16 Sender IPv6 address + for (i=0; i<16; i++) *ptr++ = spa->b[i]; + + // 0x26 Destination IPv6 address + for (i=0; i<16; i++) *ptr++ = v6dst->b[i]; + + // 0x36 NDP header + *ptr++ = op; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement + *ptr++ = 0x00; // Code + *ptr++ = 0x00; *ptr++ = 0x00; // Checksum placeholder (0x38, 0x39) + *ptr++ = flags; + *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; + + if (op == NDP_Sol) // Neighbor Solicitation. The NDP "target" is the address we seek. + { + // 0x3E NDP target. + for (i=0; i<16; i++) *ptr++ = tpa->b[i]; + // 0x4E Source Link-layer Address + // <http://www.ietf.org/rfc/rfc2461.txt> + // MUST NOT be included when the source IP address is the unspecified address. + // Otherwise, on link layers that have addresses this option MUST be included + // in multicast solicitations and SHOULD be included in unicast solicitations. + if (!mDNSIPv6AddressIsZero(*spa)) + { + *ptr++ = NDP_SrcLL; // Option Type 1 == Source Link-layer Address + *ptr++ = 0x01; // Option length 1 (in units of 8 octets) + for (i=0; i<6; i++) + if (tha) + *ptr++ = tha->b[i]; else - m->CurrentRecord = rr->next; - } - } + *ptr++ = intf->MAC.b[i]; + } + } + else // Neighbor Advertisement. The NDP "target" is the address we're giving information about. + { + // 0x3E NDP target. + for (i=0; i<16; i++) *ptr++ = spa->b[i]; + // 0x4E Target Link-layer Address + *ptr++ = NDP_TgtLL; // Option Type 2 == Target Link-layer Address + *ptr++ = 0x01; // Option length 1 (in units of 8 octets) + for (i=0; i<6; i++) + if (tha) + *ptr++ = tha->b[i]; + else + *ptr++ = intf->MAC.b[i]; + } + + // 0x4E or 0x56 Total NDP Packet length 78 or 86 bytes + m->omsg.data[0x13] = ptr - &m->omsg.data[0x36]; // Compute actual length + checksum.NotAnInteger = ~IPv6CheckSum(spa, v6dst, 0x3A, &m->omsg.data[0x36], m->omsg.data[0x13]); + m->omsg.data[0x38] = checksum.b[0]; + m->omsg.data[0x39] = checksum.b[1]; + + mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); +} + +mDNSlocal void SetupTracerOpt(const mDNS *const m, rdataOPT *const Trace) +{ + mDNSu32 DNS_VERS = _DNS_SD_H; + Trace->u.tracer.platf = m->mDNS_plat; + Trace->u.tracer.mDNSv = DNS_VERS; + + Trace->opt = kDNSOpt_Trace; + Trace->optlen = DNSOpt_TraceData_Space - 4; +} + +mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *const intf, rdataOPT *const owner) +{ + owner->u.owner.vers = 0; + owner->u.owner.seq = m->SleepSeqNum; + owner->u.owner.HMAC = m->PrimaryMAC; + owner->u.owner.IMAC = intf->MAC; + owner->u.owner.password = zeroEthAddr; + + // Don't try to compute the optlen until *after* we've set up the data fields + // Right now the DNSOpt_Owner_Space macro does not depend on the owner->u.owner being set up correctly, but in the future it might + owner->opt = kDNSOpt_Owner; + owner->optlen = DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) - 4; +} mDNSlocal void GrantUpdateCredit(AuthRecord *rr) - { - if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; - else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); - } +{ + if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; + else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); +} + +mDNSlocal mDNSBool ShouldSendGoodbyesBeforeSleep(mDNS *const m, const NetworkInterfaceInfo *intf, AuthRecord *rr) +{ + // If there are no sleep proxies, we set the state to SleepState_Sleeping explicitly + // and hence there is no need to check for Transfering state. But if we have sleep + // proxies and partially sending goodbyes for some records, we will be in Transfering + // state and hence need to make sure that we send goodbyes in that case too. Checking whether + // we are not awake handles both cases. + if ((rr->AuthFlags & AuthFlagsWakeOnly) && (m->SleepState != SleepState_Awake)) + { + debugf("ShouldSendGoodbyesBeforeSleep: marking for goodbye", ARDisplayString(m, rr)); + return mDNStrue; + } + + if (m->SleepState != SleepState_Sleeping) + return mDNSfalse; + + // If we are going to sleep and in SleepState_Sleeping, SendGoodbyes on the interface tell you + // whether you can send goodbyes or not. + if (!intf->SendGoodbyes) + { + debugf("ShouldSendGoodbyesBeforeSleep: not sending goodbye %s, int %p", ARDisplayString(m, rr), intf->InterfaceID); + return mDNSfalse; + } + else + { + debugf("ShouldSendGoodbyesBeforeSleep: sending goodbye %s, int %p", ARDisplayString(m, rr), intf->InterfaceID); + return mDNStrue; + } +} // Note about acceleration of announcements to facilitate automatic coalescing of // multiple independent threads of announcements into a single synchronized thread: @@ -3027,304 +2322,531 @@ mDNSlocal void GrantUpdateCredit(AuthRecord *rr) // 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 +// 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); - } +{ + int pktcount = 0; + AuthRecord *rr, *r2; + mDNSs32 maxExistingAnnounceInterval = 0; + const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + + m->NextScheduledResponse = m->timenow + 0x78000000; + + if (m->SleepState == SleepState_Transferring) RetrySPSRegistrations(m); + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedUnicast) + { + mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} }; + mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} }; + v4.ip.v4 = rr->v4Requester; + v6.ip.v6 = rr->v6Requester; + if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer); + if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer); + if (rr->ImmedUnicast) + { + LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr)); + rr->ImmedUnicast = mDNSfalse; + } + } + + // *** + // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on + // *** + + // Run through our list of records, and decide which ones we're going to announce on all interfaces + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); + if (TimeToAnnounceThisRecord(rr, m->timenow)) + { + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + if (!rr->WakeUp.HMAC.l[0]) + { + if (rr->AnnounceCount) rr->ImmedAnswer = mDNSInterfaceMark; // Send goodbye packet on all interfaces + } + else + { + LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); + for (r2 = rr; r2; r2=r2->next) + if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC) && + !mDNSSameEthAddress(&zeroEthAddr, &r2->WakeUp.HMAC)) + { + // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original + // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict. + if (r2->AddressProxy.type == mDNSAddrType_IPv6 && r2->AnnounceCount == WakeupCount) + { + LogSPS("NDP Announcement %2d Releasing traffic for H-MAC %.6a I-MAC %.6a %s", + r2->AnnounceCount-3, &r2->WakeUp.HMAC, &r2->WakeUp.IMAC, ARDisplayString(m,r2)); + SendNDP(m, NDP_Adv, NDP_Override, r2, &r2->AddressProxy.ip.v6, &r2->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth); + } + r2->LastAPTime = m->timenow; + // After 15 wakeups without success (maybe host has left the network) send three goodbyes instead + if (--r2->AnnounceCount <= GoodbyeCount) r2->WakeUp.HMAC = zeroEthAddr; + } + } + } + else if (ResourceRecordIsValidAnswer(rr)) + { + if (rr->AddressProxy.type) + { + if (!mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC)) + { + rr->AnnounceCount--; + rr->ThisAPInterval *= 2; + rr->LastAPTime = m->timenow; + if (rr->AddressProxy.type == mDNSAddrType_IPv4) + { + LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", + rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr); + } + else if (rr->AddressProxy.type == mDNSAddrType_IPv6) + { + LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", + rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + } + } + } + else + { + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + if (maxExistingAnnounceInterval < rr->ThisAPInterval) + maxExistingAnnounceInterval = rr->ThisAPInterval; + if (rr->UpdateBlocked) rr->UpdateBlocked = 0; + } + } + } + } + + // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one) + // Eligible records that are more than half-way to their announcement time are accelerated + for (rr = m->ResourceRecords; rr; rr=rr->next) + if ((rr->resrec.InterfaceID && rr->ImmedAnswer) || + (rr->ThisAPInterval <= maxExistingAnnounceInterval && + TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) && + !rr->AddressProxy.type && // Don't include ARP Annoucements when considering which records to accelerate + ResourceRecordIsValidAnswer(rr))) + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + + // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals + // Note: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID, + // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen) + // then all that means is that it won't get sent -- which would not be the end of the world. + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV) + for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records + if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ... + ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ... + rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ... + rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target + SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) && + (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID)) + r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too + // We also make sure we send the DeviceInfo TXT record too, if necessary + // We check for RecordType == kDNSRecordTypeShared because we don't want to tag the + // DeviceInfo TXT record onto a goodbye packet (RecordType == kDNSRecordTypeDeregistering). + if (rr->ImmedAnswer && rr->resrec.RecordType == kDNSRecordTypeShared && rr->resrec.rrtype == kDNSType_PTR) + if (ResourceRecordIsValidAnswer(&m->DeviceInfo) && SameDomainLabel(rr->resrec.rdata->u.name.c, m->DeviceInfo.resrec.name->c)) + { + if (!m->DeviceInfo.ImmedAnswer) m->DeviceInfo.ImmedAnswer = rr->ImmedAnswer; + else m->DeviceInfo.ImmedAnswer = mDNSInterfaceMark; + } + } + + // If there's a record which is supposed to be unique that we're going to send, then make sure that we give + // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class + // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a + // record, then other RRSet members that have not been sent recently will get flushed out of client caches. + // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface + // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + { + if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked + { + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2)) + if (r2->ImmedAnswer != mDNSInterfaceMark && + r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(r2, rr)) + r2->ImmedAnswer = !r2->ImmedAnswer ? rr->ImmedAnswer : mDNSInterfaceMark; + } + else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked + { + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2)) + if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(r2, rr)) + r2->ImmedAdditional = rr->ImmedAdditional; + } + } + + // Now set SendRNow state appropriately + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces + { + rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; + rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer + rr->LastMCTime = m->timenow; + rr->LastMCInterface = rr->ImmedAnswer; + // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done + if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) + { + rr->AnnounceCount--; + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) + rr->ThisAPInterval *= 2; + rr->LastAPTime = m->timenow; + debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount); + } + } + else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface: + { + rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface + rr->ImmedAdditional = mDNSNULL; // No need to send as additional too + rr->LastMCTime = m->timenow; + rr->LastMCInterface = rr->ImmedAnswer; + } + SetNextAnnounceProbeTime(m, rr); + //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr)); + } + + // *** + // *** 2. Loop through interface list, sending records as appropriate + // *** + + while (intf) + { + int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; + int TraceRecordSpace = (mDNS_McastTracingEnabled && MDNS_TRACER) ? DNSOpt_Header_Space + DNSOpt_TraceData_Space : 0; + int numDereg = 0; + int numAnnounce = 0; + int numAnswer = 0; + int AnoninfoSpace = 0; + mDNSu8 *responseptr = m->omsg.data; + mDNSu8 *newptr; + InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); + + // First Pass. Look for: + // 1. Deregistering records that need to send their goodbye packet + // 2. Updated records that need to retract their old data + // 3. Answers and announcements we need to send + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + + // Skip this interface if the record InterfaceID is *Any and the record is not + // appropriate for the interface type. + if ((rr->SendRNow == intf->InterfaceID) && + ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf))) + { + LogInfo("SendResponses: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow)); + rr->SendRNow = GetNextActiveInterfaceID(intf); + } + else if (rr->SendRNow == intf->InterfaceID) + { + RData *OldRData = rr->resrec.rdata; + mDNSu16 oldrdlength = rr->resrec.rdlength; + mDNSu8 active = (mDNSu8) + (rr->resrec.RecordType != kDNSRecordTypeDeregistering && !ShouldSendGoodbyesBeforeSleep(m, intf, rr)); + newptr = mDNSNULL; + if (rr->NewRData && active) + { + // See if we should send a courtesy "goodbye" for the old data before we replace it. + if (ResourceRecordIsValidAnswer(rr) && rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + { + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + if (newptr) { responseptr = newptr; numDereg++; rr->RequireGoodbye = mDNSfalse; } + else continue; // If this packet is already too full to hold the goodbye for this record, skip it for now and we'll retry later + } + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); + } + + if (rr->resrec.AnonInfo) + { + int tmp = AnonInfoSpace(rr->resrec.AnonInfo); + + AnoninfoSpace += tmp; + // Adjust OwnerRecordSpace/TraceRecordSpace which is used by PutRR_OS_TTL below so that + // we have space to put in the NSEC3 record in the authority section. + OwnerRecordSpace += tmp; + TraceRecordSpace += tmp; + } + + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (newptr) + { + responseptr = newptr; + rr->RequireGoodbye = active; + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) numDereg++; + else if (rr->LastAPTime == m->timenow) numAnnounce++;else numAnswer++; + } + + if (rr->NewRData && active) + SetNewRData(&rr->resrec, OldRData, oldrdlength); + + // The first time through (pktcount==0), if this record is verified unique + // (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too. + if (!pktcount && active && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow) + rr->SendNSECNow = mDNSInterfaceMark; + + if (newptr) // If succeeded in sending, advance to next interface + { + if (rr->resrec.AnonInfo) + { + debugf("SendResponses: Marking %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, + TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); + rr->resrec.AnonInfo->SendNow = intf->InterfaceID; + } + + // 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; + } + } + } + + // Get the reserved space back + OwnerRecordSpace -= AnoninfoSpace; + TraceRecordSpace -= AnoninfoSpace; + newptr = responseptr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->resrec.AnonInfo && rr->resrec.AnonInfo->SendNow == intf->InterfaceID) + { + ResourceRecord *nsec3RR = rr->resrec.AnonInfo->nsec3RR; + + newptr = PutRR_OS_TTL(newptr, &m->omsg.h.numAuthorities, nsec3RR, nsec3RR->rroriginalttl); + if (newptr) + { + responseptr = newptr; + debugf("SendResponses: Added NSEC3 %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, + TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); + } + else + { + LogMsg("SendResponses: Cannot add NSEC3 %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, + TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); + } + rr->resrec.AnonInfo->SendNow = mDNSNULL; + } + } + // Second Pass. Add additional records, if there's space. + newptr = responseptr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedAdditional == intf->InterfaceID) + if (ResourceRecordIsValidAnswer(rr)) + { + // If we have at least one answer already in the packet, then plan to add additionals too + mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0); + + // If we're not planning to send any additionals, but this record is a unique one, then + // make sure we haven't already sent any other members of its RRSet -- if we have, then they + // will have had the cache flush bit set, so now we need to finish the job and send the rest. + if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)) + { + const AuthRecord *a; + for (a = m->ResourceRecords; a; a=a->next) + if (a->LastMCTime == m->timenow && + a->LastMCInterface == intf->InterfaceID && + SameResourceRecordSignature(a, rr)) { SendAdditional = mDNStrue; break; } + } + if (!SendAdditional) // If we don't want to send this after all, + rr->ImmedAdditional = mDNSNULL; // then cancel its ImmedAdditional field + else if (newptr) // Else, try to add it if we can + { + // The first time through (pktcount==0), if this record is verified unique + // (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too. + if (!pktcount && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow) + rr->SendNSECNow = mDNSInterfaceMark; + + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutRR_OS(newptr, &m->omsg.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (newptr) + { + responseptr = newptr; + rr->ImmedAdditional = mDNSNULL; + rr->RequireGoodbye = mDNStrue; + // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface. + // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise, + // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get + // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches. + rr->LastMCTime = m->timenow; + rr->LastMCInterface = intf->InterfaceID; + } + } + } + + // Third Pass. Add NSEC records, if there's space. + // When we're generating an NSEC record in response to a specify query for that type + // (recognized by rr->SendNSECNow == intf->InterfaceID) we should really put the NSEC in the Answer Section, + // not Additional Section, but for now it's easier to handle both cases in this Additional Section loop here. + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->SendNSECNow == mDNSInterfaceMark || rr->SendNSECNow == intf->InterfaceID) + { + AuthRecord nsec; + mDNSu8 *ptr; + int len; + mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + nsec.resrec.rrclass |= kDNSClass_UniqueRRSet; + AssignDomainName(&nsec.namestorage, rr->resrec.name); + ptr = nsec.rdatastorage.u.data; + len = DomainNameLength(rr->resrec.name); + // We have a nxt name followed by window number, window length and a window bitmap + nsec.resrec.rdlength = len + 2 + NSEC_MCAST_WINDOW_SIZE; + if (nsec.resrec.rdlength <= StandardAuthRDSize) + { + mDNSPlatformMemZero(ptr, nsec.resrec.rdlength); + AssignDomainName((domainname *)ptr, rr->resrec.name); + ptr += len; + *ptr++ = 0; // window number + *ptr++ = NSEC_MCAST_WINDOW_SIZE; // window length + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2) && SameResourceRecordNameClassInterface(r2, rr)) + { + if (r2->resrec.rrtype >= kDNSQType_ANY) { LogMsg("SendResponses: Can't create NSEC for record %s", ARDisplayString(m, r2)); break; } + else ptr[r2->resrec.rrtype >> 3] |= 128 >> (r2->resrec.rrtype & 7); + } + newptr = responseptr; + if (!r2) // If we successfully built our NSEC record, add it to the packet now + { + newptr = PutRR_OS(responseptr, &m->omsg.h.numAdditionals, &nsec.resrec); + if (newptr) responseptr = newptr; + } + } + else LogMsg("SendResponses: not enough space (%d) in authrecord for nsec", nsec.resrec.rdlength); + + // If we successfully put the NSEC record, clear the SendNSECNow flag + // If we consider this NSEC optional, then we unconditionally clear the SendNSECNow flag, even if we fail to put this additional record + if (newptr || rr->SendNSECNow == mDNSInterfaceMark) + { + rr->SendNSECNow = mDNSNULL; + // Run through remainder of list clearing SendNSECNow flag for all other records which would generate the same NSEC + for (r2 = rr->next; r2; r2=r2->next) + if (SameResourceRecordNameClassInterface(r2, rr)) + if (r2->SendNSECNow == mDNSInterfaceMark || r2->SendNSECNow == intf->InterfaceID) + r2->SendNSECNow = mDNSNULL; + } + } + + if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals) + { + // If we have data to send, add OWNER/TRACER/OWNER+TRACER option if necessary, then send packet + if (OwnerRecordSpace || TraceRecordSpace) + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); + opt.resrec.rdestimate = sizeof(rdataOPT); + if (OwnerRecordSpace && TraceRecordSpace) + { + opt.resrec.rdlength += sizeof(rdataOPT); // Two options in this OPT record + opt.resrec.rdestimate += sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + SetupTracerOpt(m, &opt.resrec.rdata->u.opt[1]); + } + else if (OwnerRecordSpace) + { + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + } + else if (TraceRecordSpace) + { + SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]); + } + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec); + if (newptr) + { + responseptr = newptr; + LogInfo("SendResponses put %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt)); + } + else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1) + { + LogInfo("SendResponses: No space in packet for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + else + { + LogMsg("SendResponses: How did we fail to have space for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + } + + debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", + numDereg, numDereg == 1 ? "" : "s", + numAnnounce, numAnnounce == 1 ? "" : "s", + numAnswer, numAnswer == 1 ? "" : "s", + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); + + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); + if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } + // There might be more things to send on this interface, so go around one more time and try again. + } + else // Nothing more to send on this interface; go to next + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + #if MDNS_DEBUGMSGS && 0 + const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p"; + debugf(msg, intf, next); + #endif + intf = next; + pktcount = 0; // When we move to a new interface, reset packet count back to zero -- NSEC generation logic uses it + } + } + + // *** + // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables + // *** + + if (m->CurrentRecord) + LogMsg("SendResponses ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + + if (rr->SendRNow) + { + if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P) + LogInfo("SendResponses: No active interface %d to send: %d %02X %s", + (uint32_t)rr->SendRNow, (uint32_t)rr->resrec.InterfaceID, rr->resrec.RecordType, ARDisplayString(m, rr)); + rr->SendRNow = mDNSNULL; + } + + if (rr->ImmedAnswer || rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->AnnounceCount == 0) + { + // For Unicast, when we get the response from the server, we will call CompleteDeregistration + if (!AuthRecord_uDNS(rr)) CompleteDeregistration(m, rr); // Don't touch rr after this + } + else + { + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + } + } + } + verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow); +} // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache, // so we want to be lazy about how frequently we do it. @@ -3336,258 +2858,390 @@ mDNSlocal void SendResponses(mDNS *const m) // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets), // so allow at most 1/10 second lateness +// 5. For records with rroriginalttl set to zero, that means we really want to delete them immediately +// (we have a new record with DelayDelivery set, waiting for the old record to go away before we can notify clients). #define CacheCheckGracePeriod(RR) ( \ - ((RR)->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)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ + ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ + ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : \ + ((RR)->resrec.rroriginalttl > 0 ) ? (mDNSPlatformOneSecond/10) : 0) + +#define NextCacheCheckEvent(RR) ((RR)->NextRequiredQuery + CacheCheckGracePeriod(RR)) + +mDNSexport void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event) +{ + if (m->rrcache_nextcheck[slot] - event > 0) + m->rrcache_nextcheck[slot] = event; + if (m->NextCacheCheck - event > 0) + m->NextCacheCheck = event; +} + +// Note: MUST call SetNextCacheCheckTimeForRecord any time we change: // rr->TimeRcvd // rr->resrec.rroriginalttl // rr->UnansweredQueries // rr->CRActiveQuestion -// 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; - } +mDNSexport void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const rr) +{ + rr->NextRequiredQuery = RRExpireTime(rr); + + // If we have an active question, then see if we want to schedule a refresher query for this record. + // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL. + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + { + rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries); + rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50); + verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s", + (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr)); + } + ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr)); +} #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) #define kDefaultReconfirmTimeForWake ((mDNSu32)mDNSPlatformOneSecond * 5) -#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 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) +#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 5) +#define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 5) + +mDNSexport mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) +{ + if (interval < kMinimumReconfirmTime) + interval = kMinimumReconfirmTime; + if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below + interval = 0x10000000; + + // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration + if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3)) + { + // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts + // For all the reconfirmations in a given batch, we want to use the same random value + // so that the reconfirmation questions can be grouped into a single query packet + if (!m->RandomReconfirmDelay) m->RandomReconfirmDelay = 1 + mDNSRandom(0x3FFFFFFF); + interval += m->RandomReconfirmDelay % ((interval/3) + 1); + rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; + rr->resrec.rroriginalttl = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond; + SetNextCacheCheckTimeForRecord(m, rr); + } + debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s %p", + RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr), rr->CRActiveQuestion); + return(mStatus_NoError); +} // 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); - } + 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 anoninfo_space = q->AnonInfo ? AnonInfoSpace(q->AnonInfo) : 0; + mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast - anoninfo_space, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); + if (!newptr) + { + debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return(mDNSfalse); + } + else + { + mDNSu32 forecast = *answerforecast + anoninfo_space; + const mDNSu32 slot = HashSlot(&q->qname); + const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rr; + CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, + if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + !(rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && // which is a shared (i.e. not unique) record type + rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list + rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow > // and its half-way-to-expiry time is at least 1 second away + mDNSPlatformOneSecond) // (also ensures we never include goodbye records with TTL=1) + { + // We don't want to include unique records in the Known Answer section. The Known Answer section + // is intended to suppress floods of shared-record replies from many other devices on the network. + // That concept really does not apply to unique records, and indeed if we do send a query for + // which we have a unique record already in our cache, then including that unique record as a + // Known Answer, so as to suppress the only answer we were expecting to get, makes little sense. + + *ka = rr; // Link this record into our known answer chain + ka = &rr->NextInKAList; + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + forecast += 12 + rr->resrec.rdestimate; + // If we're trying to put more than one question in this packet, and it doesn't fit + // then undo that last question and try again next time + if (query->h.numQuestions > 1 && newptr + forecast >= limit) + { + query->h.numQuestions--; + debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d, total questions %d", + q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data, query->h.numQuestions); + ka = *kalistptrptr; // Go back to where we started and retract these answer records + while (*ka) { CacheRecord *c = *ka; *ka = mDNSNULL; ka = &c->NextInKAList; } + return(mDNSfalse); // Return false, so we'll try again in the next packet + } + } + + // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return + *queryptr = newptr; // Update the packet pointer + *answerforecast = forecast; // Update the forecast + *kalistptrptr = ka; // Update the known answer list pointer + if (ucast) q->ExpectUnicastResp = NonZeroTime(m->timenow); + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache, + if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list + SameNameRecordAnswersQuestion(&rr->resrec, q)) // which answers our question + { + rr->UnansweredQueries++; // indicate that we're expecting a response + rr->LastUnansweredTime = m->timenow; + SetNextCacheCheckTimeForRecord(m, rr); + } + + return(mDNStrue); + } +} + +// When we have a query looking for a specified name, but there appear to be no answers with +// that name, ReconfirmAntecedents() is called with depth=0 to start the reconfirmation process +// for any records in our cache that reference the given name (e.g. PTR and SRV records). +// For any such cache record we find, we also recursively call ReconfirmAntecedents() for *its* name. +// We increment depth each time we recurse, to guard against possible infinite loops, with a limit of 5. +// A typical reconfirmation scenario might go like this: +// Depth 0: Name "myhost.local" has no address records +// Depth 1: SRV "My Service._example._tcp.local." refers to "myhost.local"; may be stale +// Depth 2: PTR "_example._tcp.local." refers to "My Service"; may be stale +// Depth 3: PTR "_services._dns-sd._udp.local." refers to "_example._tcp.local."; may be stale +// Currently depths 4 and 5 are not expected to occur; if we did get to depth 5 we'd reconfim any records we +// found referring to the given name, but not recursively descend any further reconfirm *their* antecedents. +mDNSlocal void ReconfirmAntecedents(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const int depth) +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + debugf("ReconfirmAntecedents (depth=%d) for %##s", depth, name->c); + FORALL_CACHERECORDS(slot, cg, cr) + { + domainname *crtarget = GetRRDomainNameTarget(&cr->resrec); + if (crtarget && cr->resrec.rdatahash == namehash && SameDomainName(crtarget, name)) + { + LogInfo("ReconfirmAntecedents: Reconfirming (depth=%d) %s", depth, CRDisplayString(m, cr)); + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + if (depth < 5) + ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, depth+1); + } + } +} + +// If we get no answer for a AAAA query, then before doing an automatic implicit ReconfirmAntecedents +// we check if we have an address record for the same name. If we do have an IPv4 address for a given +// name but not an IPv6 address, that's okay (it just means the device doesn't do IPv6) so the failure +// to get a AAAA response is not grounds to doubt the PTR/SRV chain that lead us to that name. +mDNSlocal const CacheRecord *CacheHasAddressTypeForName(mDNS *const m, const domainname *const name, const mDNSu32 namehash) +{ + CacheGroup *const cg = CacheGroupForName(m, HashSlot(name), namehash, name); + const CacheRecord *cr = cg ? cg->members : mDNSNULL; + while (cr && !RRTypeIsAddressType(cr->resrec.rrtype)) cr=cr->next; + return(cr); +} + + +mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *const q, const CacheRecord *const c0, const CacheRecord *const c1) +{ +#ifndef SPC_DISABLED + CacheGroup *const cg = CacheGroupForName(m, HashSlot(&q->qname), q->qnamehash, &q->qname); + const CacheRecord *cr, *bestcr = mDNSNULL; + mDNSu32 bestmetric = 1000000; + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + if (cr->resrec.rrtype == kDNSType_PTR && cr->resrec.rdlength >= 6) // If record is PTR type, with long enough name, + if (cr != c0 && cr != c1) // that's not one we've seen before, + if (SameNameRecordAnswersQuestion(&cr->resrec, q)) // and answers our browse query, + if (!IdenticalSameNameRecord(&cr->resrec, &m->SPSRecords.RR_PTR.resrec)) // and is not our own advertised service... + { + mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); + if (bestmetric > metric) { bestmetric = metric; bestcr = cr; } + } + return(bestcr); +#else // SPC_DISABLED + (void) m; + (void) q; + (void) c0; + (void) c1; + (void) c1; + return mDNSNULL; +#endif // SPC_DISABLED +} + +mDNSlocal void CheckAndSwapSPS(const CacheRecord *sps1, const CacheRecord *sps2) +{ + const CacheRecord *swap_sps; + mDNSu32 metric1, metric2; + + if (!sps1 || !sps2) return; + metric1 = SPSMetric(sps1->resrec.rdata->u.name.c); + metric2 = SPSMetric(sps2->resrec.rdata->u.name.c); + if (!SPSFeatures(sps1->resrec.rdata->u.name.c) && SPSFeatures(sps2->resrec.rdata->u.name.c) && (metric2 >= metric1)) + { + swap_sps = sps1; + sps1 = sps2; + sps2 = swap_sps; + } +} + +mDNSlocal void ReorderSPSByFeature(const CacheRecord *sps[3]) +{ + CheckAndSwapSPS(sps[0], sps[1]); + CheckAndSwapSPS(sps[0], sps[2]); + CheckAndSwapSPS(sps[1], sps[2]); +} + + +// Finds the three best Sleep Proxies we currently have in our cache +mDNSexport void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const CacheRecord *sps[3]) +{ + sps[0] = FindSPSInCache1(m, q, mDNSNULL, mDNSNULL); + sps[1] = !sps[0] ? mDNSNULL : FindSPSInCache1(m, q, sps[0], mDNSNULL); + sps[2] = !sps[1] ? mDNSNULL : FindSPSInCache1(m, q, sps[0], sps[1]); + + // SPS is already sorted by metric. We want to move the entries to the beginning of the array + // only if they have equally good metric and support features. + ReorderSPSByFeature(sps); +} // 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; - } +{ + 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; - } +{ + 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); - } +{ + 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); - } +{ + int i, j; + + // See if we have this one in our list somewhere already + for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break; + + // If not, find a slot we can re-use + if (i >= DupSuppressInfoSize) + { + i = 0; + for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++) + if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0) + i = j; + } + + // Record the info about this query we saw + ds[i].Time = Time; + ds[i].InterfaceID = InterfaceID; + ds[i].Type = Type; + + return(i); +} + +mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) +{ + int len, i, cnt; + mDNSInterfaceID InterfaceID = q->InterfaceID; + domainname *d = &q->qname; + + // We can't send magic packets without knowing which interface to send it on. + if (InterfaceID == mDNSInterface_Any || InterfaceID == mDNSInterface_LocalOnly || InterfaceID == mDNSInterface_P2P) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Invalid InterfaceID %p for question %##s", InterfaceID, q->qname.c); + return; + } + + // Split MAC@IPAddress and pass them separately + len = d->c[0]; + i = 1; + cnt = 0; + for (i = 1; i < len; i++) + { + if (d->c[i] == '@') + { + char EthAddr[18]; // ethernet adddress : 12 bytes + 5 ":" + 1 NULL byte + char IPAddr[47]; // Max IP address len: 46 bytes (IPv6) + 1 NULL byte + if (cnt != 5) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, cnt %d", q->qname.c, cnt); + return; + } + if ((i - 1) > (int) (sizeof(EthAddr) - 1)) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, length %d", q->qname.c, i - 1); + return; + } + if ((len - i) > (int)(sizeof(IPAddr) - 1)) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed IP address %##s, length %d", q->qname.c, len - i); + return; + } + mDNSPlatformMemCopy(EthAddr, &d->c[1], i - 1); + EthAddr[i - 1] = 0; + mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i); + IPAddr[len - i] = 0; + m->mDNSStats.WakeOnResolves++; + mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount); + return; + } + else if (d->c[i] == ':') + cnt++; + } + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c); +} + mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) - { - // If more than 90% of the way to the query time, we should unconditionally accelerate it - if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10)) - return(mDNStrue); - - // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet - if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2)) - { - // We forecast: qname (n) type (2) class (2) - mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4; - const mDNSu32 slot = HashSlot(&q->qname); - 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); - } +{ + // If more than 90% of the way to the query time, we should unconditionally accelerate it + if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10)) + return(mDNStrue); + + // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet + if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2)) + { + // We forecast: qname (n) type (2) class (2) + mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4; + const mDNSu32 slot = HashSlot(&q->qname); + const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + const CacheRecord *rr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, + if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry + rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0) // and we'll ask at least once again before NextRequiredQuery + { + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + forecast += 12 + rr->resrec.rdestimate; + if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate + } + return(mDNStrue); + } + + return(mDNSfalse); +} // How Standard Queries are generated: // 1. The Question Section contains the question @@ -3602,316 +3256,580 @@ mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) // 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; - } - } +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + AuthRecord *ar; + int pktcount = 0; + DNSQuestion *q; + // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval + mDNSs32 maxExistingQuestionInterval = 0; + const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + CacheRecord *KnownAnswerList = mDNSNULL; + + // 1. If time for a query, work out what we need to do + + // We're expecting to send a query anyway, so see if any expiring cache records are close enough + // to their NextRequiredQuery to be worth batching them together with this one + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries) + { + if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0) + { + debugf("Sending %d%% cache expiration query for %s", 80 + 5 * cr->UnansweredQueries, CRDisplayString(m, cr)); + q = cr->CRActiveQuestion; + ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(cr)/20, cr->resrec.InterfaceID); + // For uDNS queries (TargetQID non-zero) we adjust LastQTime, + // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly + if (q->Target.type) + { + q->SendQNow = mDNSInterfaceMark; // If targeted query, mark it + } + else if (!mDNSOpaque16IsZero(q->TargetQID)) + { + q->LastQTime = m->timenow - q->ThisQInterval; + cr->UnansweredQueries++; + m->mDNSStats.CacheRefreshQueries++; + } + else if (q->SendQNow == mDNSNULL) + { + q->SendQNow = cr->resrec.InterfaceID; + } + else if (q->SendQNow != cr->resrec.InterfaceID) + { + q->SendQNow = mDNSInterfaceMark; + } + + // Indicate that this question was marked for sending + // to update an existing cached answer record. + // The browse throttling logic below uses this to determine + // if the query should be sent. + if (mDNSOpaque16IsZero(q->TargetQID)) + q->CachedAnswerNeedsUpdate = mDNStrue; + } + } + } + + // Scan our list of questions to see which: + // *WideArea* queries need to be sent + // *unicast* queries need to be sent + // *multicast* queries we're definitely going to send + if (m->CurrentQuestion) + LogMsg("SendQueries ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + q = m->CurrentQuestion; + if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) + { + mDNSu8 *qptr = m->omsg.data; + const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); + + // If we fail to get a new on-demand socket (should only happen cases of the most extreme resource exhaustion), we'll try again next time + if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (q->LocalSocket) + { + InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); + qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); + mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass); + q->ThisQInterval *= QuestionIntervalStep; + } + if (q->ThisQInterval > MaxQuestionInterval) + q->ThisQInterval = MaxQuestionInterval; + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->SendQNow = mDNSNULL; + q->ExpectUnicastResp = NonZeroTime(m->timenow); + } + else if (mDNSOpaque16IsZero(q->TargetQID) && !q->Target.type && TimeToSendThisQuestion(q, m->timenow)) + { + //LogInfo("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + if (maxExistingQuestionInterval < q->ThisQInterval) + maxExistingQuestionInterval = q->ThisQInterval; + } + // If m->CurrentQuestion wasn't modified out from under us, advance it now + // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() depends on having + // m->CurrentQuestion point to the right question + if (q == m->CurrentQuestion) m->CurrentQuestion = m->CurrentQuestion->next; + } + while (m->CurrentQuestion) + { + LogInfo("SendQueries question loop 1: Skipping NewQuestion %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->CurrentQuestion->next; + } + m->CurrentQuestion = mDNSNULL; + + // Scan our list of questions + // (a) to see if there are any more that are worth accelerating, and + // (b) to update the state variables for *all* the questions we're going to send + // Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list, + // which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very + // next thing we do is scan the list and call SetNextQueryTime() for every question we find, so we know we end up with the right value. + m->NextScheduledQuery = m->timenow + 0x78000000; + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + { + if (mDNSOpaque16IsZero(q->TargetQID) + && (q->SendQNow || (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) + { + // If at least halfway to next query time, advance to next interval + // If less than halfway to next query time, then + // treat this as logically a repeat of the last transmission, without advancing the interval + if (m->timenow - (q->LastQTime + (q->ThisQInterval/2)) >= 0) + { + // If we have reached the answer threshold for this question, + // don't send it again until MaxQuestionInterval unless: + // one of its cached answers needs to be refreshed, + // or it's the initial query for a kDNSServiceFlagsThresholdFinder mode browse. + if (q->BrowseThreshold + && (q->CurrentAnswers >= q->BrowseThreshold) + && (q->CachedAnswerNeedsUpdate == mDNSfalse) + && !((q->flags & kDNSServiceFlagsThresholdFinder) && (q->ThisQInterval == InitialQuestionInterval))) + { + q->SendQNow = mDNSNULL; + q->ThisQInterval = MaxQuestionInterval; + q->LastQTime = m->timenow; + q->RequestUnicast = 0; + LogInfo("SendQueries: (%s) %##s reached threshold of %d answers", + DNSTypeName(q->qtype), q->qname.c, q->BrowseThreshold); + } + else + { + // Mark this question for sending on all interfaces + q->SendQNow = mDNSInterfaceMark; + q->ThisQInterval *= QuestionIntervalStep; + } + + debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d", + q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast); + + if (q->ThisQInterval >= QuestionIntervalThreshold) + { + q->ThisQInterval = MaxQuestionInterval; + } + else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast && + !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash))) + { + // Generally don't need to log this. + // It's not especially noteworthy if a query finds no results -- this usually happens for domain + // enumeration queries in the LL subdomain (e.g. "db._dns-sd._udp.0.0.254.169.in-addr.arpa") + // and when there simply happen to be no instances of the service the client is looking + // for (e.g. iTunes is set to look for RAOP devices, and the current network has none). + debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", + q->qname.c, DNSTypeName(q->qtype)); + // Sending third query, and no answers yet; time to begin doubting the source + ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); + } + } + + // Mark for sending. (If no active interfaces, then don't even try.) + q->SendOnAll = (q->SendQNow == mDNSInterfaceMark); + if (q->SendOnAll) + { + q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID; + q->LastQTime = m->timenow; + } + + // If we recorded a duplicate suppression for this question less than half an interval ago, + // then we consider it recent enough that we don't need to do an identical query ourselves. + ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); + + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + if (q->RequestUnicast) q->RequestUnicast--; + } + // For all questions (not just the ones we're sending) check what the next scheduled event will be + // We don't need to consider NewQuestions here because for those we'll set m->NextScheduledQuery in AnswerNewQuestion + SetNextQueryTime(m,q); + } + + // 2. Scan our authoritative RR list to see what probes we might need to send + + m->NextScheduledProbe = m->timenow + 0x78000000; + + if (m->CurrentRecord) + LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + ar = m->CurrentRecord; + m->CurrentRecord = ar->next; + if (!AuthRecord_uDNS(ar) && ar->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing... + { + // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly + if (m->timenow - (ar->LastAPTime + ar->ThisAPInterval) < 0) + { + SetNextAnnounceProbeTime(m, ar); + } + // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly + else if (ar->ProbeCount) + { + if (ar->AddressProxy.type == mDNSAddrType_IPv4) + { + // There's a problem here. If a host is waking up, and we probe to see if it responds, then + // it will see those ARP probes as signalling intent to use the address, so it picks a different one. + // A more benign way to find out if a host is responding to ARPs might be send a standard ARP *request* + // (using our sender IP address) instead of an ARP *probe* (using all-zero sender IP address). + // A similar concern may apply to the NDP Probe too. -- SC + LogSPS("SendQueries ARP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); + SendARP(m, 1, ar, &zerov4Addr, &zeroEthAddr, &ar->AddressProxy.ip.v4, &ar->WakeUp.IMAC); + } + else if (ar->AddressProxy.type == mDNSAddrType_IPv6) + { + LogSPS("SendQueries NDP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); + // IPv6 source = zero + // No target hardware address + // IPv6 target address is address we're probing + // Ethernet destination address is Ethernet interface address of the Sleep Proxy client we're probing + SendNDP(m, NDP_Sol, 0, ar, &zerov6Addr, mDNSNULL, &ar->AddressProxy.ip.v6, &ar->WakeUp.IMAC); + } + // Mark for sending. (If no active interfaces, then don't even try.) + ar->SendRNow = (!intf || ar->WakeUp.HMAC.l[0]) ? mDNSNULL : ar->resrec.InterfaceID ? ar->resrec.InterfaceID : intf->InterfaceID; + ar->LastAPTime = m->timenow; + // When we have a late conflict that resets a record to probing state we use a special marker value greater + // than DefaultProbeCountForTypeUnique. Here we detect that state and reset ar->ProbeCount back to the right value. + if (ar->ProbeCount > DefaultProbeCountForTypeUnique) + ar->ProbeCount = DefaultProbeCountForTypeUnique; + ar->ProbeCount--; + SetNextAnnounceProbeTime(m, ar); + if (ar->ProbeCount == 0) + { + // If this is the last probe for this record, then see if we have any matching records + // on our duplicate list which should similarly have their ProbeCount cleared to zero... + AuthRecord *r2; + for (r2 = m->DuplicateRecords; r2; r2=r2->next) + if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, ar)) + r2->ProbeCount = 0; + // ... then acknowledge this record to the client. + // We do this optimistically, just as we're about to send the third probe. + // This helps clients that both advertise and browse, and want to filter themselves + // from the browse results list, because it helps ensure that the registration + // confirmation will be delivered 1/4 second *before* the browse "add" event. + // A potential downside is that we could deliver a registration confirmation and then find out + // moments later that there's a name conflict, but applications have to be prepared to handle + // late conflicts anyway (e.g. on connection of network cable, etc.), so this is nothing new. + if (!ar->Acknowledged) AcknowledgeRecord(m, ar); + } + } + // else, if it has now finished probing, move it to state Verified, + // and update m->NextScheduledResponse so it will be announced + else + { + if (!ar->Acknowledged) AcknowledgeRecord(m, ar); // Defensive, just in case it got missed somehow + ar->resrec.RecordType = kDNSRecordTypeVerified; + ar->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; + ar->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; + SetNextAnnounceProbeTime(m, ar); + } + } + } + m->CurrentRecord = m->DuplicateRecords; + while (m->CurrentRecord) + { + ar = m->CurrentRecord; + m->CurrentRecord = ar->next; + if (ar->resrec.RecordType == kDNSRecordTypeUnique && ar->ProbeCount == 0 && !ar->Acknowledged) + AcknowledgeRecord(m, ar); + } + + // 3. Now we know which queries and probes we're sending, + // go through our interface list sending the appropriate queries on each interface + while (intf) + { + int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; + int TraceRecordSpace = (mDNS_McastTracingEnabled && MDNS_TRACER) ? DNSOpt_Header_Space + DNSOpt_TraceData_Space : 0; + mDNSu8 *queryptr = m->omsg.data; + mDNSBool useBackgroundTrafficClass = mDNSfalse; // set if we should use background traffic class + + InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); + if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); + if (!KnownAnswerList) + { + // Start a new known-answer list + CacheRecord **kalistptr = &KnownAnswerList; + mDNSu32 answerforecast = OwnerRecordSpace + TraceRecordSpace; // Start by assuming we'll need at least enough space to put the Owner+Tracer Option + + // Put query questions in this packet + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + { + if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow == intf->InterfaceID)) + { + mDNSBool Suppress = mDNSfalse; + 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 interface is P2P type, verify that query should be sent over it. + if (!mDNSPlatformValidQuestionForInterface(q, intf)) + { + LogInfo("SendQueries: Not sending (%s) %##s on %s", DNSTypeName(q->qtype), q->qname.c, InterfaceNameForID(m, intf->InterfaceID)); + q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); + } + // If we're suppressing this question, or we successfully put it, update its SendQNow state + else if ((Suppress = SuppressOnThisInterface(q->DupSuppress, intf)) || + BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) + { + // We successfully added the question to the packet. Make sure that + // we also send the NSEC3 record if required. BuildQuestion accounted for + // the space. + // + // Note: We don't suppress anonymous questions and hence Suppress should always + // be zero. + + if (Suppress) + m->mDNSStats.DupQuerySuppressions++; + + if (!Suppress && q->AnonInfo) + { + debugf("SendQueries: marking for question %##s, Suppress %d", q->qname.c, Suppress); + q->AnonInfo->SendNow = intf->InterfaceID; + } + q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); + if (q->WakeOnResolveCount) + { + mDNSSendWakeOnResolve(m, q); + q->WakeOnResolveCount--; + } + + // use brackground traffic class if any included question requires it + if (q->UseBackgroundTrafficClass) + { + useBackgroundTrafficClass = mDNStrue; + } + } + } + } + + // Put probe questions in this packet + for (ar = m->ResourceRecords; ar; ar=ar->next) + if (ar->SendRNow == intf->InterfaceID) + { + mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; + mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); + const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + mDNSu32 forecast = answerforecast + 12 + ar->resrec.rdestimate; + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, (mDNSu16)(ar->resrec.rrclass | ucbit)); + if (newptr) + { + queryptr = newptr; + answerforecast = forecast; + ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + ar->IncludeInProbe = mDNStrue; + verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", + ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount); + } + } + } + + // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) + while (KnownAnswerList) + { + CacheRecord *ka = KnownAnswerList; + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond; + mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, + m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace - TraceRecordSpace); + if (newptr) + { + verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", + ka->resrec.name->c, DNSTypeName(ka->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); + queryptr = newptr; + KnownAnswerList = ka->NextInKAList; + ka->NextInKAList = mDNSNULL; + } + else + { + // If we ran out of space and we have more than one question in the packet, that's an error -- + // we shouldn't have put more than one question if there was a risk of us running out of space. + if (m->omsg.h.numQuestions > 1) + LogMsg("SendQueries: Put %d answers; No more space for known answers", m->omsg.h.numAnswers); + m->omsg.h.flags.b[0] |= kDNSFlag0_TC; + break; + } + } + + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + if (ar->IncludeInProbe) + { + mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &ar->resrec); + ar->IncludeInProbe = mDNSfalse; + if (newptr) queryptr = newptr; + else LogMsg("SendQueries: How did we fail to have space for the Update record %s", ARDisplayString(m,ar)); + } + } + + for (q = m->Questions; q; q = q->next) + { + if (q->AnonInfo && q->AnonInfo->SendNow == intf->InterfaceID) + { + mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, q->AnonInfo->nsec3RR); + if (newptr) + { + debugf("SendQueries: Added NSEC3 record %s on InterfaceID %p", RRDisplayString(m, q->AnonInfo->nsec3RR), intf->InterfaceID); + queryptr = newptr; + } + else + { + LogMsg("SendQueries: ERROR!! Cannot add NSEC3 record %s on InterfaceID %p", RRDisplayString(m, q->AnonInfo->nsec3RR), intf->InterfaceID); + } + q->AnonInfo->SendNow = mDNSNULL; + } + } + + if (queryptr > m->omsg.data) + { + // If we have data to send, add OWNER/TRACER/OWNER+TRACER option if necessary, then send packet + if (OwnerRecordSpace || TraceRecordSpace) + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); + opt.resrec.rdestimate = sizeof(rdataOPT); + if (OwnerRecordSpace && TraceRecordSpace) + { + opt.resrec.rdlength += sizeof(rdataOPT); // Two options in this OPT record + opt.resrec.rdestimate += sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + SetupTracerOpt(m, &opt.resrec.rdata->u.opt[1]); + } + else if (OwnerRecordSpace) + { + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + } + else if (TraceRecordSpace) + { + SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]); + } + LogInfo("SendQueries putting %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt)); + queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, + &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); + if (!queryptr) + { + LogMsg("SendQueries: How did we fail to have space for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + if (queryptr > m->omsg.data + NormalMaxDNSMessageData) + { + if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1) + LogMsg("SendQueries: Why did we generate oversized packet with %s %s OPT record %p %p %p (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", + TraceRecordSpace ? "TRACER" : "", m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, m->omsg.h.numQuestions, m->omsg.h.numAnswers, + m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + } + + if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) + LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); + debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", + m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", + m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", + m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL, useBackgroundTrafficClass); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL, useBackgroundTrafficClass); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); + if (++pktcount >= 1000) + { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; } + // There might be more records left in the known answer list, or more questions to send + // on this interface, so go around one more time and try again. + } + else // Nothing more to send on this interface; go to next + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + #if MDNS_DEBUGMSGS && 0 + const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p"; + debugf(msg, intf, next); + #endif + intf = next; + } + } + + // 4. Final housekeeping + + // 4a. Debugging check: Make sure we announced all our records + for (ar = m->ResourceRecords; ar; ar=ar->next) + if (ar->SendRNow) + { + if (ar->ARType != AuthRecordLocalOnly && ar->ARType != AuthRecordP2P) + LogInfo("SendQueries: No active interface %d to send probe: %d %s", + (uint32_t)ar->SendRNow, (uint32_t)ar->resrec.InterfaceID, ARDisplayString(m, ar)); + ar->SendRNow = mDNSNULL; + } + + // 4b. When we have lingering cache records that we're keeping around for a few seconds in the hope + // that their interface which went away might come back again, the logic will want to send queries + // for those records, but we can't because their interface isn't here any more, so to keep the + // state machine ticking over we just pretend we did so. + // If the interface does not come back in time, the cache record will expire naturally + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries) + { + if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0) + { + cr->UnansweredQueries++; + cr->CRActiveQuestion->SendQNow = mDNSNULL; + SetNextCacheCheckTimeForRecord(m, cr); + } + } + } + + // 4c. Debugging check: Make sure we sent all our planned questions + // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions + // we legitimately couldn't send because the interface is no longer available + for (q = m->Questions; q; q=q->next) + { + if (q->SendQNow) + { + DNSQuestion *x; + for (x = m->NewQuestions; x; x=x->next) if (x == q) break; // Check if this question is a NewQuestion + LogInfo("SendQueries: No active interface %d to send %s question: %d %##s (%s)", + (uint32_t)q->SendQNow, x ? "new" : "old", (uint32_t)q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + q->SendQNow = mDNSNULL; + } + q->CachedAnswerNeedsUpdate = mDNSfalse; + } +} + +mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password) +{ + int i, j; + mDNSu8 *ptr = m->omsg.data; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found", InterfaceID); return; } + + // 0x00 Destination address + for (i=0; i<6; i++) *ptr++ = EthAddr->b[i]; + + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0]; + + // 0x0C Ethertype (0x0842) + *ptr++ = 0x08; + *ptr++ = 0x42; + + // 0x0E Wakeup sync sequence + for (i=0; i<6; i++) *ptr++ = 0xFF; + + // 0x14 Wakeup data + for (j=0; j<16; j++) for (i=0; i<6; i++) *ptr++ = EthAddr->b[i]; + + // 0x74 Password + for (i=0; i<6; i++) *ptr++ = password->b[i]; + + mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); + + // For Ethernet switches that don't flood-foward packets with unknown unicast destination MAC addresses, + // broadcast is the only reliable way to get a wakeup packet to the intended target machine. + // For 802.11 WPA networks, where a sleeping target machine may have missed a broadcast/multicast + // key rotation, unicast is the only way to get a wakeup packet to the intended target machine. + // So, we send one of each, unicast first, then broadcast second. + for (i=0; i<6; i++) m->omsg.data[i] = 0xFF; + mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -3919,143 +3837,313 @@ mDNSlocal void SendQueries(mDNS *const m) #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. - } +// Whenever a question is answered, reset its state so that we don't query +// the network repeatedly. This happens first time when we answer the question and +// and later when we refresh the cache. +mDNSlocal void ResetQuestionState(mDNS *const m, DNSQuestion *q) +{ + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->ThisQInterval = MaxQuestionInterval; + q->RequestUnicast = 0; + // Reset unansweredQueries so that we don't penalize this server later when we + // start sending queries when the cache expires. + q->unansweredQueries = 0; + debugf("ResetQuestionState: Set MaxQuestionInterval for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); +} + +// Note: AnswerCurrentQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list. +// Any code walking either list must use the m->CurrentQuestion (and possibly m->CurrentRecord) mechanism to protect against this. +// In fact, to enforce this, the routine will *only* answer the question currently pointed to by m->CurrentQuestion, +// which will be auto-advanced (possibly to NULL) if the client callback cancels the question. +mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord) +{ + DNSQuestion *const q = m->CurrentQuestion; + mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord); + + verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s", + q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr)); + + // When the response for the question was validated, the entire rrset was validated. If we deliver + // a RMV for a single record in the rrset, we invalidate the response. If we deliver another add + // in the future, we will do the revalidation again. + // + // Also, if we deliver an ADD for a negative cache record and it has no NSEC/NSEC3, the ValidationStatus needs + // to be reset. This happens normally when we deliver a "secure" negative response followed by an insecure + // negative response which can happen e.g., when disconnecting from network that leads to a negative response + // due to no DNS servers. As we don't deliver RMVs for negative responses that were delivered before, we need + // to do it on the next ADD of a negative cache record. This ADD could be the result of a timeout, no DNS servers + // etc. in which case we need to reset the state to make sure we don't deliver them as secure. If this is + // a real negative response, we would reset the state here and validate the results at the end of this function. + // or the real response again if we purge the cache. + if (q->ValidationRequired && ((AddRecord == QC_rmv) || + (rr->resrec.RecordType == kDNSRecordTypePacketNegative && (AddRecord == QC_add)))) + { + q->ValidationStatus = 0; + q->ValidationState = DNSSECValRequired; + } + + // Normally we don't send out the unicast query if we have answered using our local only auth records e.g., /etc/hosts. + // But if the query for "A" record has a local answer but query for "AAAA" record has no local answer, we might + // send the AAAA query out which will come back with CNAME and will also answer the "A" query. To prevent that, + // we check to see if that query already has a unique local answer. + if (q->LOAddressAnswers) + { + LogInfo("AnswerCurrentQuestionWithResourceRecord: Question %p %##s (%s) not answering with record %s due to " + "LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr), + q->LOAddressAnswers); + return; + } + + if (QuerySuppressed(q)) + { + // If the query is suppressed, then we don't want to answer from the cache. But if this query is + // supposed to time out, we still want to callback the clients. We do this only for TimeoutQuestions + // that are timing out, which we know are answered with negative cache record when timing out. + if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0)) + return; + } + + // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue) + // may be called twice, once when the record is received, and again when it's time to notify local clients. + // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this. + + rr->LastUsed = m->timenow; + if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q) + { + if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count + debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion from %p to %p for cache record %s, CurrentAnswer %d", + rr->CRActiveQuestion, q, CRDisplayString(m,rr), q->CurrentAnswers); + rr->CRActiveQuestion = q; // We know q is non-null + SetNextCacheCheckTimeForRecord(m, rr); + } + + // If this is: + // (a) a no-cache add, where we've already done at least one 'QM' query, or + // (b) a normal add, where we have at least one unique-type answer, + // then there's no need to keep polling the network. + // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.) + // We do this for mDNS questions and uDNS one-shot questions, but not for + // uDNS LongLived questions, because that would mess up our LLQ lease renewal timing. + if ((AddRecord == QC_addnocache && !q->RequestUnicast) || + (AddRecord == QC_add && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)))) + if (ActiveQuestion(q) && (mDNSOpaque16IsZero(q->TargetQID) || !q->LongLived)) + { + ResetQuestionState(m, q); + } + + if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us + + // Only deliver negative answers if client has explicitly requested them except when we are forcing a negative response + // for the purpose of retrying search domains/timeout OR the question is suppressed + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype))) + if (!AddRecord || (AddRecord != QC_suppressed && AddRecord != QC_forceresponse && !q->ReturnIntermed)) return; + + // For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that + if (q->QuestionCallback && !q->NoAnswer && (!followcname || q->ReturnIntermed)) + { + mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls + if (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype)) + { + CacheRecord neg; + MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID, q->qDNSServer); + q->QuestionCallback(m, q, &neg.resrec, AddRecord); + } + else + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + // If this is an "Add" operation and this question needs validation, validate the response. + // In the case of negative responses, extra care should be taken. Negative cache records are + // used for many purposes. For example, + // + // 1) Suppressing questions (SuppressUnusable) + // 2) Timeout questions + // 3) The name does not exist + // 4) No DNS servers are available and we need a quick response for the application + // + // (1) and (2) are handled by "QC_add" check as AddRecord would be "QC_forceresponse" or "QC_suppressed" + // in that case. For (3), it is possible that we don't get nsecs back but we still need to call + // VerifySignature so that we can deliver the appropriate DNSSEC result. There is no point in verifying + // signature for (4) and hence the explicit check for q->qDNSServer. + // + if (m->CurrentQuestion == q && (AddRecord == QC_add) && !q->ValidatingResponse && q->ValidationRequired && + q->ValidationState == DNSSECValRequired && q->qDNSServer) + { + q->ValidationState = DNSSECValInProgress; + // Treat it as callback call as that's what dnssec code expects + mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls + VerifySignature(m, mDNSNULL, q); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + return; + } + + // Note: Proceed with caution here because client callback function is allowed to do anything, + // including starting/stopping queries, registering/deregistering records, etc. + // + // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG), + // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated + // first before following it + if (!ValidatingQuestion(q) && followcname && m->CurrentQuestion == q) + AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); +} 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. +{ + rr->DelayDelivery = 0; + if (m->CurrentQuestion) + LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} + +mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot, mDNSBool *purge) +{ + const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second + const mDNSs32 start = m->timenow - 0x10000000; + mDNSs32 delay = start; + CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); + const CacheRecord *rr; + + if (purge) + *purge = mDNSfalse; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + // If there are records that will expire soon, there are cases that need delayed + // delivery of events: + // + // 1) A new cache entry is about to be added as a replacement. The caller needs to + // deliver a RMV (for the current old entry) followed by ADD (for the new entry). + // It needs to schedule the timer for the next cache expiry (ScheduleNextCacheCheckTime), + // so that the cache entry can be purged (purging causes the RMV followed by ADD) + // + // 2) A new question is about to be answered and the caller needs to know whether it's + // scheduling should be delayed so that the question is not answered with this record. + // Instead of delivering an ADD (old entry) followed by RMV (old entry) and another ADD + // (new entry), a single ADD can be delivered by delaying the scheduling of the question + // immediately. + // + // When the unicast cache record is created, it's TTL has been extended beyond its value + // given in the resource record (See RRAdjustTTL). If it is in the "extended" time, the + // cache is already expired and we set "purge" to indicate that. When "purge" is set, the + // return value of the function should be ignored by the callers. + // + // Note: For case (1), "purge" argument is NULL and hence the following checks are skipped. + // It is okay to skip in that case because the cache records have been set to expire almost + // immediately and the extended time does not apply. + // + // Also, if there is already an active question we don't try to optimize as purging the cache + // would end up delivering RMV for the active question and hence we avoid that. + + if (purge && !rr->resrec.InterfaceID && !rr->CRActiveQuestion && rr->resrec.rroriginalttl) + { + mDNSu32 uTTL = RRUnadjustedTTL(rr->resrec.rroriginalttl); + if (m->timenow - (rr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0) + { + LogInfo("CheckForSoonToExpireRecords: %s: rroriginalttl %u, unadjustedTTL %u, currentTTL %u", + CRDisplayString(m, rr), rr->resrec.rroriginalttl, uTTL, (m->timenow - rr->TimeRcvd)/mDNSPlatformOneSecond); + *purge = mDNStrue; + continue; + } + } + if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second + { + if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted + delay = RRExpireTime(rr); + } + } + if (delay - start > 0) + return(NonZeroTime(delay)); + else + return(0); +} + +// CacheRecordAdd is only called from CreateNewCacheEntry, *never* directly as a result of a client API call. // If new questions are created as a result of invoking client callbacks, they will be added to // the end of the question list, and m->NewQuestions will be set to indicate the first new question. // rr is a new CacheRecord just received into our cache // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). -// NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback, +// Note: CacheRecordAdd calls AnswerCurrentQuestionWithResourceRecord which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) - { - 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); - } +{ + DNSQuestion *q; + + // We stop when we get to NewQuestions -- if we increment their CurrentAnswers/LargeAnswers/UniqueAnswers + // counters here we'll end up double-incrementing them when we do it again in AnswerNewQuestion(). + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + { + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + // If this question is one that's actively sending queries, and it's received ten answers within one + // second of sending the last query packet, then that indicates some radical network topology change, + // so reset its exponential backoff back to the start. We must be at least at the eight-second interval + // to do this. If we're at the four-second interval, or less, there's not much benefit accelerating + // because we will anyway send another query within a few seconds. The first reset query is sent out + // randomized over the next four seconds to reduce possible synchronization between machines. + if (q->LastAnswerPktNum != m->PktNum) + { + q->LastAnswerPktNum = m->PktNum; + if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 && + q->ThisQInterval > InitialQuestionInterval * QuestionIntervalStep3 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) + { + LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst (%d); restarting exponential backoff sequence (%d)", + q->qname.c, DNSTypeName(q->qtype), q->RecentAnswerPkts, q->ThisQInterval); + q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); + q->ThisQInterval = InitialQuestionInterval; + SetNextQueryTime(m,q); + } + } + verbosedebugf("CacheRecordAdd %p %##s (%s) %lu %#a:%d question %p", rr, rr->resrec.name->c, + DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl, rr->resrec.rDNSServer ? + &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ? + rr->resrec.rDNSServer->port : zeroIPPort), q); + q->CurrentAnswers++; + + q->unansweredQueries = 0; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + if (q->CurrentAnswers > 4000) + { + static int msgcount = 0; + if (msgcount++ < 10) + LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack", + q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers); + rr->resrec.rroriginalttl = 0; + rr->UnansweredQueries = MaxUnansweredQueries; + } + } + } + + if (!rr->DelayDelivery) + { + if (m->CurrentQuestion) + LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + q = m->CurrentQuestion; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; + } + + SetNextCacheCheckTimeForRecord(m, rr); +} // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. // If new questions are created as a result of invoking client callbacks, they will be added to @@ -4064,616 +4152,2607 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) // 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, +// Note: NoCacheAnswer calls AnswerCurrentQuestionWithResourceRecord which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) - { - LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c); - if (m->CurrentQuestion) LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set"); - 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 +{ + LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c); + if (m->CurrentQuestion) + LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + // We do this for *all* questions, not stopping when we get to m->NewQuestions, + // since we're not caching the record and we'll get no opportunity to do this later + while (m->CurrentQuestion) + { + DNSQuestion *q = m->CurrentQuestion; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_addnocache); // QC_addnocache means "don't expect remove events for this" + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} + +// CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute. +// Note that CacheRecordRmv is *only* called for records that are referenced by at least one active question. // If new questions are created as a result of invoking client callbacks, they will be added to // the end of the question list, and m->NewQuestions will be set to indicate the first new question. // rr is an existing cache CacheRecord that just expired and is being deleted // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). -// NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback, +// Note: CacheRecordRmv calls AnswerCurrentQuestionWithResourceRecord which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) - { - if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set"); - 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; - } +{ + if (m->CurrentQuestion) + LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + + // We stop when we get to NewQuestions -- for new questions their CurrentAnswers/LargeAnswers/UniqueAnswers counters + // will all still be zero because we haven't yet gone through the cache counting how many answers we have for them. + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + // When a question enters suppressed state, we generate RMV events and generate a negative + // response. A cache may be present that answers this question e.g., cache entry generated + // before the question became suppressed. We need to skip the suppressed questions here as + // the RMV event has already been generated. + if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr)); + q->FlappingInterface1 = mDNSNULL; + q->FlappingInterface2 = mDNSNULL; + + if (q->CurrentAnswers == 0) { + mDNSIPPort zp = zeroIPPort; + LogMsg("CacheRecordRmv ERROR!!: How can CurrentAnswers already be zero for %p %##s (%s) DNSServer %#a:%d", + q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, + mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zp)); + } + else + { + q->CurrentAnswers--; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + } + + // If we have dropped below the answer threshold for this mDNS question, + // restart the queries at InitialQuestionInterval. + if (mDNSOpaque16IsZero(q->TargetQID) && (q->BrowseThreshold > 0) && (q->CurrentAnswers < q->BrowseThreshold)) + { + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + SetNextQueryTime(m,q); + LogInfo("CacheRecordRmv: (%s) %##s dropped below threshold of %d answers", + DNSTypeName(q->qtype), q->qname.c, q->BrowseThreshold); + } + if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results + { + if (q->CurrentAnswers == 0) + { + LogInfo("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents", + q->qname.c, DNSTypeName(q->qtype)); + ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); + } + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); + } + } + if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e) - { -#if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 - unsigned int i; - for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF; +{ +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + unsigned int i; + for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF; #endif - e->next = m->rrcache_free; - m->rrcache_free = e; - m->rrcache_totalused--; - } + 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); - } +{ + 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 ReleaseAdditionalCacheRecords(mDNS *const m, CacheRecord **rp) +{ + while (*rp) + { + CacheRecord *rr = *rp; + *rp = (*rp)->next; // Cut record from list + if (rr->resrec.rdata && rr->resrec.rdata != (RData*)&rr->smallrdatastorage) + { + mDNSPlatformMemFree(rr->resrec.rdata); + rr->resrec.rdata = mDNSNULL; + } + // NSEC or SOA records that are not added to the CacheGroup do not share the name + // of the CacheGroup. + if (rr->resrec.name) + { + debugf("ReleaseAdditionalCacheRecords: freeing cached record %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + mDNSPlatformMemFree((void *)rr->resrec.name); + rr->resrec.name = mDNSNULL; + } + // Don't count the NSEC3 records used by anonymous browse/reg + if (!rr->resrec.InterfaceID) + { + m->rrcache_totalused_unicast -= rr->resrec.rdlength; + if (DNSSECRecordType(rr->resrec.rrtype)) + BumpDNSSECStats(m, kStatsActionDecrement, kStatsTypeMemoryUsage, rr->resrec.rdlength); + } + ReleaseCacheEntity(m, (CacheEntity *)rr); + } +} + +mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) +{ + CacheGroup *cg; + const mDNSu32 slot = HashSlot(r->resrec.name); + + //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r)); + if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata); + r->resrec.rdata = mDNSNULL; + + cg = CacheGroupForRecord(m, slot, &r->resrec); + + if (!cg) + { + // It is okay to have this printed for NSEC/NSEC3s + LogInfo("ReleaseCacheRecord: ERROR!! cg NULL for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); + } + // When NSEC records are not added to the cache, it is usually cached at the "nsec" list + // of the CacheRecord. But sometimes they may be freed without adding to the "nsec" list + // (which is handled below) and in that case it should be freed here. + if (r->resrec.name && cg && r->resrec.name != cg->name) + { + debugf("ReleaseCacheRecord: freeing %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); + mDNSPlatformMemFree((void *)r->resrec.name); + } + r->resrec.name = mDNSNULL; + + if (r->resrec.AnonInfo) + { + debugf("ReleaseCacheRecord: freeing AnonInfo for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); + FreeAnonInfo((void *)r->resrec.AnonInfo); + } + r->resrec.AnonInfo = mDNSNULL; + + if (!r->resrec.InterfaceID) + { + m->rrcache_totalused_unicast -= r->resrec.rdlength; + if (DNSSECRecordType(r->resrec.rrtype)) + BumpDNSSECStats(m, kStatsActionDecrement, kStatsTypeMemoryUsage, r->resrec.rdlength); + } + + ReleaseAdditionalCacheRecords(m, &r->nsec); + ReleaseAdditionalCacheRecords(m, &r->soa); + + 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 CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGroup *const cg) +{ + CacheRecord **rp = &cg->members; + + if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; } + m->lock_rrcache = 1; + + while (*rp) + { + CacheRecord *const rr = *rp; + mDNSs32 event = RRExpireTime(rr); + if (m->timenow - event >= 0) // If expired, delete it + { + *rp = rr->next; // Cut it from the list + + verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s", + m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); + if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away + { + DNSQuestion *q = rr->CRActiveQuestion; + // When a cache record is about to expire, we expect to do four queries at 80-82%, 85-87%, 90-92% and + // then 95-97% of the TTL. If the DNS server does not respond, then we will remove the cache entry + // before we pick a new DNS server. As the question interval is set to MaxQuestionInterval, we may + // not send out a query anytime soon. Hence, we need to reset the question interval. If this is + // a normal deferred ADD case, then AnswerCurrentQuestionWithResourceRecord will reset it to + // MaxQuestionInterval. If we have inactive questions referring to negative cache entries, + // don't ressurect them as they will deliver duplicate "No such Record" ADD events + if (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived && ActiveQuestion(q)) + { + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + SetNextQueryTime(m, q); + } + CacheRecordRmv(m, rr); + m->rrcache_active--; + } + ReleaseCacheRecord(m, rr); + } + else // else, not expired; see if we need to query + { + // If waiting to delay delivery, do nothing until then + if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) + event = rr->DelayDelivery; + else + { + if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr); + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + { + if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query + event = NextCacheCheckEvent(rr); // then just record when we want the next query + else // else trigger our question to go out now + { + // Set NextScheduledQuery to timenow so that SendQueries() will run. + // SendQueries() will see that we have records close to expiration, and send FEQs for them. + m->NextScheduledQuery = m->timenow; + // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTimeForRecord(), + // which will correctly update m->NextCacheCheck for us. + event = m->timenow + 0x3FFFFFFF; + } + } + } + verbosedebugf("CheckCacheExpiration:%6d %5d %s", + (event - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr)); + if (m->rrcache_nextcheck[slot] - event > 0) + m->rrcache_nextcheck[slot] = event; + rp = &rr->next; + } + } + if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp); + cg->rrcache_tail = rp; + m->lock_rrcache = 0; +} + +// "LORecord" includes both LocalOnly and P2P record. This function assumes m->CurrentQuestion is pointing to "q". +// +// If "CheckOnly" is set to "true", the question won't be answered but just check to see if there is an answer and +// returns true if there is an answer. +// +// If "CheckOnly" is set to "false", the question will be answered if there is a LocalOnly/P2P record and +// returns true to indicate the same. +mDNSlocal mDNSBool AnswerQuestionWithLORecord(mDNS *const m, DNSQuestion *q, mDNSBool checkOnly) +{ + mDNSu32 slot; + AuthRecord *lr; + AuthGroup *ag; + + if (m->CurrentRecord) + LogMsg("AnswerQuestionWithLORecord ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + m->CurrentRecord = ag->members; + while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + // + // If the question is mDNSInterface_LocalOnly, all records local to the machine should be used + // to answer the query. This is handled in AnswerNewLocalOnlyQuestion. + // + // We handle mDNSInterface_Any and scoped questions here. See LocalOnlyRecordAnswersQuestion for more + // details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly + // we handle both mDNSInterface_Any and scoped questions. + + if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any)) + if (LocalOnlyRecordAnswersQuestion(rr, q)) + { + if (checkOnly) + { + LogInfo("AnswerQuestionWithLORecord: question %##s (%s) answered by %s", q->qname.c, DNSTypeName(q->qtype), + ARDisplayString(m, rr)); + m->CurrentRecord = mDNSNULL; + return mDNStrue; + } + AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_add); + if (m->CurrentQuestion != q) + break; // If callback deleted q, then we're finished here + } + } + } + m->CurrentRecord = mDNSNULL; + + if (m->CurrentQuestion != q) + { + LogInfo("AnswerQuestionWithLORecord: Question deleted while while answering LocalOnly record answers"); + return mDNStrue; + } + + if (q->LOAddressAnswers) + { + LogInfo("AnswerQuestionWithLORecord: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d", + q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); + return mDNStrue; + } + + // Before we go check the cache and ship this query on the wire, we have to be sure that there are + // no local records that could possibly answer this question. As we did not check the NewLocalRecords, we + // need to just peek at them to see whether it will answer this question. If it would answer, pretend + // that we answered. AnswerAllLocalQuestionsWithLocalAuthRecord will answer shortly. This happens normally + // when we add new /etc/hosts entries and restart the question. It is a new question and also a new record. + if (ag) + { + lr = ag->NewLocalOnlyRecords; + while (lr) + { + if (UniqueLocalOnlyRecord(lr) && LocalOnlyRecordAnswersQuestion(lr, q)) + { + LogInfo("AnswerQuestionWithLORecord: Question %p %##s (%s) will be answered using new local auth records " + " LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); + return mDNStrue; + } + lr = lr->next; + } + } + return mDNSfalse; +} + +// Today, we suppress questions (not send them on the wire) for several reasons e.g., +// AAAA query is suppressed because no IPv6 capability or PID is not allowed to make +// DNS requests. We need to temporarily suspend the suppress status so that we can +// deliver a negative response (AnswerCurrentQuestionWithResourceRecord does not answer +// suppressed questions) and reset it back. In the future, if there are other +// reasons for suppressing the query, this function should be updated. +mDNSlocal void AnswerSuppressedQuestion(mDNS *const m, DNSQuestion *q) +{ + mDNSBool SuppressQuery = q->SuppressQuery; + mDNSBool DisallowPID = q->DisallowPID; + + // make sure that QuerySuppressed() returns false + q->SuppressQuery = mDNSfalse; + q->DisallowPID = mDNSfalse; + + GenerateNegativeResponse(m, QC_suppressed); + + q->SuppressQuery = SuppressQuery; + q->DisallowPID = DisallowPID; +} 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; - } +{ + mDNSBool ShouldQueryImmediately = mDNStrue; + DNSQuestion *const q = m->NewQuestions; // Grab the question we're going to answer + mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + mDNSBool AnsweredFromCache = mDNSfalse; + + verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (cg) CheckCacheExpiration(m, slot, cg); + if (m->NewQuestions != q) { LogInfo("AnswerNewQuestion: Question deleted while doing CheckCacheExpiration"); goto exit; } + m->NewQuestions = q->next; + // Advance NewQuestions to the next *after* calling CheckCacheExpiration, because if we advance it first + // then CheckCacheExpiration may give this question add/remove callbacks, and it's not yet ready for that. + // + // Also, CheckCacheExpiration() calls CacheRecordDeferredAdd() and CacheRecordRmv(), which invoke + // client callbacks, which may delete their own or any other question. Our mechanism for detecting + // whether our current m->NewQuestions question got deleted by one of these callbacks is to store the + // value of m->NewQuestions in 'q' before calling CheckCacheExpiration(), and then verify afterwards + // that they're still the same. If m->NewQuestions has changed (because mDNS_StopQuery_internal + // advanced it), that means the question was deleted, so we no longer need to worry about answering + // it (and indeed 'q' is now a dangling pointer, so dereferencing it at all would be bad, and the + // values we computed for slot and cg are now stale and relate to a question that no longer exists). + // + // We can't use the usual m->CurrentQuestion mechanism for this because CacheRecordDeferredAdd() and + // CacheRecordRmv() both use that themselves when walking the list of (non-new) questions generating callbacks. + // Fortunately mDNS_StopQuery_internal auto-advances both m->CurrentQuestion *AND* m->NewQuestions when + // deleting a question, so luckily we have an easy alternative way of detecting if our question got deleted. + + if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!"); + // This should be safe, because calling the client's question callback may cause the + // question list to be modified, but should not ever cause the rrcache list to be modified. + // If the client's question callback deletes the question, then m->CurrentQuestion will + // be advanced, and we'll exit out of the loop + m->lock_rrcache = 1; + if (m->CurrentQuestion) + LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted + + if (q->NoAnswer == NoAnswer_Fail) + { + LogMsg("AnswerNewQuestion: NoAnswer_Fail %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->qDNSServer); + q->NoAnswer = NoAnswer_Normal; // Temporarily turn off answer suppression + AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache); + // Don't touch the question if it has been stopped already + if (m->CurrentQuestion == q) q->NoAnswer = NoAnswer_Fail; // Restore NoAnswer state + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + if (m->CurrentQuestion != q) + { + LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); + goto exit; + } + + // See if we want to tell it about LocalOnly/P2P records. If we answered them using LocalOnly + // or P2P record, then we are done. + if (AnswerQuestionWithLORecord(m, q, mDNSfalse)) + goto exit; + + // If we are not supposed to answer this question, generate a negative response. + // Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question + // + // If it is a question trying to validate some response, it already checked the cache for a response. If it still + // reissues a question it means it could not find the RRSIGs. So, we need to bypass the cache check and send + // the question out. + if (QuerySuppressed(q)) + { + AnswerSuppressedQuestion(m, q); + } + else if (!q->ValidatingResponse) + { + CacheRecord *rr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + // SecsSinceRcvd is whole number of elapsed seconds, rounded down + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; + if (rr->resrec.rroriginalttl <= SecsSinceRcvd) + { + LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %s %d %d", + rr->resrec.rroriginalttl, SecsSinceRcvd, CRDisplayString(m, rr), m->timenow, rr->TimeRcvd); + continue; // Go to next one in loop + } + + // If this record set is marked unique, then that means we can reasonably assume we have the whole set + // -- we don't need to rush out on the network and query immediately to see if there are more answers out there + if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) + ShouldQueryImmediately = mDNSfalse; + q->CurrentAnswers++; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + AnsweredFromCache = mDNStrue; + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) + ShouldQueryImmediately = mDNSfalse; + } + // We don't use LogInfo for this "Question deleted" message because it happens so routinely that + // it's not remotely remarkable, and therefore unlikely to be of much help tracking down bugs. + if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving cache answers"); goto exit; } + + // Neither a local record nor a cache entry could answer this question. If this question need to be retried + // with search domains, generate a negative response which will now retry after appending search domains. + // If the query was suppressed above, we already generated a negative response. When it gets unsuppressed, + // we will retry with search domains. + if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains) + { + LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + GenerateNegativeResponse(m, QC_forceresponse); + } + + if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; } + + // Note: When a query gets suppressed or retried with search domains, we de-activate the question. + // Hence we don't execute the following block of code for those cases. + if (ShouldQueryImmediately && ActiveQuestion(q)) + { + debugf("AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + if (mDNSOpaque16IsZero(q->TargetQID)) // For mDNS, spread packets to avoid a burst of simultaneous queries + { + // Compute random delay in the range 1-6 seconds, then divide by 50 to get 20-120ms + if (!m->RandomQueryDelay) + m->RandomQueryDelay = (mDNSPlatformOneSecond + mDNSRandom(mDNSPlatformOneSecond*5) - 1) / 50 + 1; + q->LastQTime += m->RandomQueryDelay; + } + } + + // IN ALL CASES make sure that m->NextScheduledQuery is set appropriately. + // In cases where m->NewQuestions->DelayAnswering is set, we may have delayed generating our + // answers for this question until *after* its scheduled transmission time, in which case + // m->NextScheduledQuery may now be set to 'never', and in that case -- even though we're *not* doing + // ShouldQueryImmediately -- we still need to make sure we set m->NextScheduledQuery correctly. + SetNextQueryTime(m,q); + +exit: + m->CurrentQuestion = mDNSNULL; + m->lock_rrcache = 0; +} // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any -// appropriate answers, stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions +// appropriate answers, stopping if it reaches a NewLocalOnlyRecord -- these will be handled by AnswerAllLocalQuestionsWithLocalAuthRecord 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; - } +{ + mDNSu32 slot; + AuthGroup *ag; + DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer + m->NewLocalOnlyQuestions = q->next; // Advance NewLocalOnlyQuestions to the next (if any) + + debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (m->CurrentQuestion) + LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted + + if (m->CurrentRecord) + LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + + // 1. First walk the LocalOnly records answering the LocalOnly question + // 2. As LocalOnly questions should also be answered by any other Auth records local to the machine, + // walk the ResourceRecords list delivering the answers + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + m->CurrentRecord = ag->members; + while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (LocalOnlyRecordAnswersQuestion(rr, q)) + { + AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_add); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } + } + + if (m->CurrentQuestion == q) + { + m->CurrentRecord = m->ResourceRecords; + + while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_add); + 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); - } +{ + CacheEntity *e = mDNSNULL; + + if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } + m->lock_rrcache = 1; + + // If we have no free records, ask the client layer to give us some more memory + if (!m->rrcache_free && m->MainCallback) + { + if (m->rrcache_totalused != m->rrcache_size) + LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu", + m->rrcache_totalused, m->rrcache_size); + + // We don't want to be vulnerable to a malicious attacker flooding us with an infinite + // number of bogus records so that we keep growing our cache until the machine runs out of memory. + // To guard against this, if our cache grows above 512kB (approx 3168 records at 164 bytes each), + // and we're actively using less than 1/32 of that cache, then we purge all the unused records + // and recycle them, instead of allocating more memory. + if (m->rrcache_size > 5000 && m->rrcache_size / 32 > m->rrcache_active) + LogInfo("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", + m->rrcache_size, m->rrcache_active); + else + { + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + m->MainCallback(m, mStatus_GrowCache); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + } + + // If we still have no free records, recycle all the records we can. + // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass. + if (!m->rrcache_free) + { + mDNSu32 oldtotalused = m->rrcache_totalused; + mDNSu32 slot; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + CacheGroup **cp = &m->rrcache_hash[slot]; + while (*cp) + { + CacheRecord **rp = &(*cp)->members; + while (*rp) + { + // Records that answer still-active questions are not candidates for recycling + // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash + if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList) + rp=&(*rp)->next; + else + { + CacheRecord *rr = *rp; + *rp = (*rp)->next; // Cut record from list + ReleaseCacheRecord(m, rr); + } + } + if ((*cp)->rrcache_tail != rp) + verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp); + (*cp)->rrcache_tail = rp; + if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next; + else ReleaseCacheGroup(m, cp); + } + } + LogInfo("GetCacheEntity recycled %d records to reduce cache from %d to %d", + oldtotalused - m->rrcache_totalused, oldtotalused, m->rrcache_totalused); + } + + if (m->rrcache_free) // If there are records in the free list, take one + { + e = m->rrcache_free; + m->rrcache_free = e->next; + if (++m->rrcache_totalused >= m->rrcache_report) + { + LogInfo("RR Cache now using %ld objects", m->rrcache_totalused); + if (m->rrcache_report < 100) m->rrcache_report += 10; + else if (m->rrcache_report < 1000) m->rrcache_report += 100; + else m->rrcache_report += 1000; + } + mDNSPlatformMemZero(e, sizeof(*e)); + } + + m->lock_rrcache = 0; + + return(e); +} mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength) - { - CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg); - if (r) - { - r->resrec.rdata = (RData*)&r->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); - } +{ + CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg); + if (r) + { + r->resrec.rdata = (RData*)&r->smallrdatastorage; // By default, assume we're usually going to be using local storage + if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage + { + r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); + if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; + else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; } + } + } + return(r); +} mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) - { - mDNSu16 namelen = DomainNameLength(rr->name); - CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL); - if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } - cg->next = m->rrcache_hash[slot]; - cg->namehash = rr->namehash; - cg->members = mDNSNULL; - cg->rrcache_tail = &cg->members; - cg->name = (domainname*)cg->namestorage; - //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", - // (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c); - if (namelen > InlineCacheGroupNameSize) cg->name = mDNSPlatformMemAllocate(namelen); - if (!cg->name) - { - LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c); - ReleaseCacheEntity(m, (CacheEntity*)cg); - return(mDNSNULL); - } - AssignDomainName(cg->name, rr->name); - - if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c); - m->rrcache_hash[slot] = cg; - if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c); - - return(cg); - } - -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); - } +{ + 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; + if (namelen > sizeof(cg->namestorage)) + cg->name = mDNSPlatformMemAllocate(namelen); + else + cg->name = (domainname*)cg->namestorage; + if (!cg->name) + { + LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c); + ReleaseCacheEntity(m, (CacheEntity*)cg); + return(mDNSNULL); + } + AssignDomainName(cg->name, rr->name); + + if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c); + m->rrcache_hash[slot] = cg; + if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c); + + return(cg); +} + +mDNSexport void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) +{ + mDNS_CheckLock(m); + + // Make sure we mark this record as thoroughly expired -- we don't ever want to give + // a positive answer using an expired record (e.g. from an interface that has gone away). + // We don't want to clear CRActiveQuestion here, because that would leave the record subject to + // summary deletion without giving the proper callback to any questions that are monitoring it. + // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries. + rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60; + rr->UnansweredQueries = MaxUnansweredQueries; + rr->resrec.rroriginalttl = 0; + SetNextCacheCheckTimeForRecord(m, rr); +} mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) - { - mDNSs32 time; - mDNSPlatformLock(m); - if (m->mDNS_busy) - { - LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow."); - if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); - } - - if (m->timenow) time = m->timenow; - else time = mDNS_TimeNow_NoLock(m); - mDNSPlatformUnlock(m); - return(time); - } +{ + mDNSs32 time; + mDNSPlatformLock(m); + if (m->mDNS_busy) + { + LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow."); + if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); + } + + if (m->timenow) time = m->timenow; + else time = mDNS_TimeNow_NoLock(m); + mDNSPlatformUnlock(m); + return(time); +} + +// To avoid pointless CPU thrash, we use SetSPSProxyListChanged(X) to record the last interface that +// had its Sleep Proxy client list change, and defer to actual BPF reconfiguration to mDNS_Execute(). +// (GetNextScheduledEvent() returns "now" when m->SPSProxyListChanged is set) +#define SetSPSProxyListChanged(X) do { \ + if (m->SPSProxyListChanged && m->SPSProxyListChanged != (X)) mDNSPlatformUpdateProxyList(m, m->SPSProxyListChanged); \ + m->SPSProxyListChanged = (X); } while(0) + +// Called from mDNS_Execute() to expire stale proxy records +mDNSlocal void CheckProxyRecords(mDNS *const m, AuthRecord *list) +{ + m->CurrentRecord = list; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering && rr->WakeUp.HMAC.l[0]) + { + // If m->SPSSocket is NULL that means we're not acting as a sleep proxy any more, + // so we need to cease proxying for *all* records we may have, expired or not. + if (m->SPSSocket && m->timenow - rr->TimeExpire < 0) // If proxy record not expired yet, update m->NextScheduledSPS + { + if (m->NextScheduledSPS - rr->TimeExpire > 0) + m->NextScheduledSPS = rr->TimeExpire; + } + else // else proxy record expired, so remove it + { + LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s", + m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr)); + SetSPSProxyListChanged(rr->resrec.InterfaceID); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + // Don't touch rr after this -- memory may have been free'd + } + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} + +mDNSlocal void CheckRmvEventsForLocalRecords(mDNS *const m) +{ + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + debugf("CheckRmvEventsForLocalRecords: Generating local RMV events for %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeShared; + AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, QC_rmv); + if (m->CurrentRecord == rr) // If rr still exists in list, restore its state now + { + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + rr->AnsweredLocalQ = mDNSfalse; + // SendResponses normally calls CompleteDeregistration after sending goodbyes. + // For LocalOnly records, we don't do that and hence we need to do that here. + if (RRLocalOnly(rr)) CompleteDeregistration(m, rr); + } + } + if (m->CurrentRecord == rr) // If m->CurrentRecord was not auto-advanced, do it ourselves now + m->CurrentRecord = rr->next; + } +} + +mDNSlocal void TimeoutQuestions(mDNS *const m) +{ + m->NextScheduledStopTime = m->timenow + 0x3FFFFFFF; + if (m->CurrentQuestion) + LogMsg("TimeoutQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, + DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion) + { + DNSQuestion *const q = m->CurrentQuestion; + if (q->StopTime) + { + if (!q->TimeoutQuestion) + LogMsg("TimeoutQuestions: ERROR!! TimeoutQuestion not set, but StopTime set for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (m->timenow - q->StopTime >= 0) + { + LogInfo("TimeoutQuestions: question %p %##s timed out, time %d", q, q->qname.c, m->timenow - q->StopTime); + GenerateNegativeResponse(m, QC_forceresponse); + if (m->CurrentQuestion == q) q->StopTime = 0; + } + else + { + if (m->NextScheduledStopTime - q->StopTime > 0) + m->NextScheduledStopTime = q->StopTime; + } + } + // If m->CurrentQuestion wasn't modified out from under us, advance it now + // We can't do this at the start of the loop because GenerateNegativeResponse + // depends on having m->CurrentQuestion point to the right question + if (m->CurrentQuestion == q) + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} + +mDNSlocal void mDNSCoreFreeProxyRR(mDNS *const m) +{ + AuthRecord *rrPtr = m->SPSRRSet, *rrNext = mDNSNULL; + LogSPS("%s : Freeing stored sleep proxy A/AAAA records", __func__); + while (rrPtr) + { + rrNext = rrPtr->next; + mDNSPlatformMemFree(rrPtr); + rrPtr = rrNext; + } + m->SPSRRSet = mDNSNULL; +} mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) - { - mDNS_Lock(m); // Must grab lock before trying to read m->timenow +{ + mDNS_Lock(m); // Must grab lock before trying to read m->timenow + +#if APPLE_OSX_mDNSResponder + mDNSLogStatistics(m); +#endif // APPLE_OSX_mDNSResponder + + if (m->timenow - m->NextScheduledEvent >= 0) + { + int i; + AuthRecord *head, *tail; + mDNSu32 slot; + AuthGroup *ag; + + verbosedebugf("mDNS_Execute"); + + if (m->CurrentQuestion) + LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + if (m->CurrentRecord) + LogMsg("mDNS_Execute: ERROR m->CurrentRecord already set: %s", ARDisplayString(m, m->CurrentRecord)); + + // 1. If we're past the probe suppression time, we can clear it + if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0; + + // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter + if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0; + + // 3. Purge our cache of stale old records + if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0) + { + mDNSu32 numchecked = 0; + m->NextCacheCheck = m->timenow + 0x3FFFFFFF; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + if (m->timenow - m->rrcache_nextcheck[slot] >= 0) + { + CacheGroup **cp = &m->rrcache_hash[slot]; + m->rrcache_nextcheck[slot] = m->timenow + 0x3FFFFFFF; + while (*cp) + { + debugf("m->NextCacheCheck %4d Slot %3d %##s", numchecked, slot, *cp ? (*cp)->name : (domainname*)"\x04NULL"); + numchecked++; + CheckCacheExpiration(m, slot, *cp); + if ((*cp)->members) cp=&(*cp)->next; + else ReleaseCacheGroup(m, cp); + } + } + // Even if we didn't need to actually check this slot yet, still need to + // factor its nextcheck time into our overall NextCacheCheck value + if (m->NextCacheCheck - m->rrcache_nextcheck[slot] > 0) + m->NextCacheCheck = m->rrcache_nextcheck[slot]; + } + debugf("m->NextCacheCheck %4d checked, next in %d", numchecked, m->NextCacheCheck - m->timenow); + } + + if (m->timenow - m->NextScheduledSPS >= 0) + { + m->NextScheduledSPS = m->timenow + 0x3FFFFFFF; + CheckProxyRecords(m, m->DuplicateRecords); // Clear m->DuplicateRecords first, then m->ResourceRecords + CheckProxyRecords(m, m->ResourceRecords); + } + + SetSPSProxyListChanged(mDNSNULL); // Perform any deferred BPF reconfiguration now + + // Check to see if we need to send any keepalives. Do this after we called CheckProxyRecords above + // as records could have expired during that check + if (m->timenow - m->NextScheduledKA >= 0) + { + m->NextScheduledKA = m->timenow + 0x3FFFFFFF; + mDNS_SendKeepalives(m); + } + + // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) + if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) + { + m->AnnounceOwner = 0; + } + + if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) + { + m->DelaySleep = 0; + if (m->SleepState == SleepState_Transferring) + { + LogSPS("Re-sleep delay passed; now checking for Sleep Proxy Servers"); + BeginSleepProcessing(m); + } + } + + // 4. See if we can answer any of our new local questions from the cache + for (i=0; m->NewQuestions && i<1000; i++) + { + if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break; + AnswerNewQuestion(m); + } + if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); + + // Make sure we deliver *all* local RMV events, and clear the corresponding rr->AnsweredLocalQ flags, *before* + // we begin generating *any* new ADD events in the m->NewLocalOnlyQuestions and m->NewLocalRecords loops below. + for (i=0; i<1000 && m->LocalRemoveEvents; i++) + { + m->LocalRemoveEvents = mDNSfalse; + m->CurrentRecord = m->ResourceRecords; + CheckRmvEventsForLocalRecords(m); + // Walk the LocalOnly records and deliver the RMV events + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + { + m->CurrentRecord = ag->members; + if (m->CurrentRecord) CheckRmvEventsForLocalRecords(m); + } + } + + if (i >= 1000) LogMsg("mDNS_Execute: m->LocalRemoveEvents exceeded loop limit"); + + for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); + if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); + + head = tail = mDNSNULL; + for (i=0; i<1000 && m->NewLocalRecords && m->NewLocalRecords != head; i++) + { + AuthRecord *rr = m->NewLocalRecords; + m->NewLocalRecords = m->NewLocalRecords->next; + if (LocalRecordReady(rr)) + { + debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr)); + AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, QC_add); + } + else if (!rr->next) + { + // If we have just one record that is not ready, we don't have to unlink and + // reinsert. As the NewLocalRecords will be NULL for this case, the loop will + // terminate and set the NewLocalRecords to rr. + debugf("mDNS_Execute: Just one LocalAuthRecord %s, breaking out of the loop early", ARDisplayString(m, rr)); + if (head != mDNSNULL || m->NewLocalRecords != mDNSNULL) + LogMsg("mDNS_Execute: ERROR!!: head %p, NewLocalRecords %p", head, m->NewLocalRecords); + + head = rr; + } + else + { + AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records + debugf("mDNS_Execute: Skipping LocalAuthRecord %s", ARDisplayString(m, rr)); + // if this is the first record we are skipping, move to the end of the list. + // if we have already skipped records before, append it at the end. + while (*p && *p != rr) p=&(*p)->next; + if (*p) *p = rr->next; // Cut this record from the list + else { LogMsg("mDNS_Execute: ERROR!! Cannot find record %s in ResourceRecords list", ARDisplayString(m, rr)); break; } + if (!head) + { + while (*p) p=&(*p)->next; + *p = rr; + head = tail = rr; + } + else + { + tail->next = rr; + tail = rr; + } + rr->next = mDNSNULL; + } + } + m->NewLocalRecords = head; + debugf("mDNS_Execute: Setting NewLocalRecords to %s", (head ? ARDisplayString(m, head) : "NULL")); + + if (i >= 1000) LogMsg("mDNS_Execute: m->NewLocalRecords exceeded loop limit"); + + // Check to see if we have any new LocalOnly/P2P records to examine for delivering + // to our local questions + if (m->NewLocalOnlyRecords) + { + m->NewLocalOnlyRecords = mDNSfalse; + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + { + for (i=0; i<100 && ag->NewLocalOnlyRecords; i++) + { + AuthRecord *rr = ag->NewLocalOnlyRecords; + ag->NewLocalOnlyRecords = ag->NewLocalOnlyRecords->next; + // LocalOnly records should always be ready as they never probe + if (LocalRecordReady(rr)) + { + debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr)); + AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, QC_add); + } + else LogMsg("mDNS_Execute: LocalOnlyRecord %s not ready", ARDisplayString(m, rr)); + } + // We limit about 100 per AuthGroup that can be serviced at a time + if (i >= 100) LogMsg("mDNS_Execute: ag->NewLocalOnlyRecords exceeded loop limit"); + } + } + + // 5. See what packets we need to send + if (m->mDNSPlatformStatus != mStatus_NoError || (m->SleepState == SleepState_Sleeping)) + DiscardDeregistrations(m); + if (m->mDNSPlatformStatus == mStatus_NoError && (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0)) + { + // If the platform code is ready, and we're not suppressing packet generation right now + // then send our responses, probes, and questions. + // We check the cache first, because there might be records close to expiring that trigger questions to refresh them. + // We send queries next, because there might be final-stage probes that complete their probing here, causing + // them to advance to announcing state, and we want those to be included in any announcements we send out. + // Finally, we send responses, including the previously mentioned records that just completed probing. + m->SuppressSending = 0; + + // 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) + { + DNSQuestion *q; + LogMsg("mDNS_Execute: SendQueries didn't send all its queries (%d - %d = %d) will try again in one second", + m->timenow, m->NextScheduledQuery, m->timenow - m->NextScheduledQuery); + m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond; + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + if (ActiveQuestion(q) && m->timenow - NextQSendTime(q) >= 0) + LogMsg("mDNS_Execute: SendQueries didn't send %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + } + if (m->timenow - m->NextScheduledProbe >= 0) + { + LogMsg("mDNS_Execute: SendQueries didn't send all its probes (%d - %d = %d) will try again in one second", + m->timenow, m->NextScheduledProbe, m->timenow - m->NextScheduledProbe); + m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond; + } + + // 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; + + if (m->NextScheduledStopTime && m->timenow - m->NextScheduledStopTime >= 0) TimeoutQuestions(m); +#ifndef UNICAST_DISABLED + if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) UpdateAllSRVRecords(m); + if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m); + if (m->timenow - m->NextuDNSEvent >= 0) uDNS_Tasks(m); +#endif + } + + // Note about multi-threaded systems: + // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(), + // performing mDNS API operations that change our next scheduled event time. + // + // On multi-threaded systems (like the current Windows implementation) that have a single main thread + // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility + // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will + // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one + // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful + // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it + // does, the state of the signal will be noticed, causing the blocking primitive to return immediately + // without blocking. This avoids the race condition between the signal from the other thread arriving + // just *before* or just *after* the main thread enters the blocking primitive. + // + // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven, + // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to + // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer + // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale + // by the time it gets to the timer callback function). + + mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value + return(m->NextScheduledEvent); +} + +#ifndef UNICAST_DISABLED +mDNSlocal void SuspendLLQs(mDNS *m) +{ + DNSQuestion *q; + for (q = m->Questions; q; q = q->next) + if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->state == LLQ_Established) + { q->ReqLease = 0; sendLLQRefresh(m, q); } +} +#endif // UNICAST_DISABLED - if (m->timenow - m->NextScheduledEvent >= 0) - { - int i; +mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q) +{ + AuthRecord *rr; + mDNSu32 slot; + AuthGroup *ag; + + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + for (rr = ag->members; rr; rr=rr->next) + // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME + if (UniqueLocalOnlyRecord(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) + { + LogInfo("QuestionHasLocalAnswers: Question %p %##s (%s) has local answer %s", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr)); + return mDNStrue; + } + } + return mDNSfalse; +} + +// ActivateUnicastQuery() is called from three places: +// 1. When a new question is created +// 2. On wake from sleep +// 3. When the DNS configuration changes +// In case 1 we don't want to mess with our established ThisQInterval and LastQTime (ScheduleImmediately is false) +// In cases 2 and 3 we do want to cause the question to be resent immediately (ScheduleImmediately is true) +mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question, mDNSBool ScheduleImmediately) +{ + // For now this AutoTunnel stuff is specific to Mac OS X. + // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer +#if APPLE_OSX_mDNSResponder + // Even though BTMM client tunnels are only useful for AAAA queries, we need to treat v4 and v6 queries equally. + // Otherwise we can get the situation where the A query completes really fast (with an NXDOMAIN result) and the + // caller then gives up waiting for the AAAA result while we're still in the process of setting up the tunnel. + // To level the playing field, we block both A and AAAA queries while tunnel setup is in progress, and then + // returns results for both at the same time. If we are looking for the _autotunnel6 record, then skip this logic + // as this would trigger looking up _autotunnel6._autotunnel6 and end up failing the original query. + + if (RRTypeIsAddressType(question->qtype) && PrivateQuery(question) && + !SameDomainLabel(question->qname.c, (const mDNSu8 *)"\x0c_autotunnel6")&& question->QuestionCallback != AutoTunnelCallback) + { + question->NoAnswer = NoAnswer_Suspended; + AddNewClientTunnel(m, question); + return; + } +#endif // APPLE_OSX_mDNSResponder + + if (!question->DuplicateOf) + { + debugf("ActivateUnicastQuery: %##s %s%s%s", + question->qname.c, DNSTypeName(question->qtype), PrivateQuery(question) ? " (Private)" : "", ScheduleImmediately ? " ScheduleImmediately" : ""); + question->CNAMEReferrals = 0; + if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } + if (question->LongLived) + { + question->state = LLQ_InitialRequest; + question->id = zeroOpaque64; + question->servPort = zeroIPPort; + if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } + } + // If the question has local answers, then we don't want answers from outside + if (ScheduleImmediately && !QuestionHasLocalAnswers(m, question)) + { + question->ThisQInterval = InitialQuestionInterval; + question->LastQTime = m->timenow - question->ThisQInterval; + SetNextQueryTime(m, question); + } + } +} + +// Caller should hold the lock +mDNSexport void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords, + CallbackBeforeStartQuery BeforeStartCallback, void *context) +{ + DNSQuestion *q; + DNSQuestion *restart = mDNSNULL; + + mDNS_CheckLock(m); + + // 1. Flush the cache records + if (flushCacheRecords) flushCacheRecords(m); + + // 2. Even though we may have purged the cache records above, before it can generate RMV event + // we are going to stop the question. Hence we need to deliver the RMV event before we + // stop the question. + // + // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the + // application callback can potentially stop the current question (detected by CurrentQuestion) or + // *any* other question which could be the next one that we may process here. RestartQuestion + // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal + // if the "next" question is stopped while the CurrentQuestion is stopped + + if (m->RestartQuestion) + LogMsg("mDNSCoreRestartAddressQueries: ERROR!! m->RestartQuestion already set: %##s (%s)", + m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype)); + + m->RestartQuestion = m->Questions; + while (m->RestartQuestion) + { + q = m->RestartQuestion; + m->RestartQuestion = q->next; + // GetZoneData questions are referenced by other questions (original query that started the GetZoneData + // question) through their "nta" pointer. Normally when the original query stops, it stops the + // GetZoneData question and also frees the memory (See CancelGetZoneData). If we stop the GetZoneData + // question followed by the original query that refers to this GetZoneData question, we will end up + // freeing the GetZoneData question and then start the "freed" question at the end. + + if (IsGetZoneDataQuestion(q)) + { + DNSQuestion *refq = q->next; + LogInfo("mDNSCoreRestartAddressQueries: Skipping GetZoneDataQuestion %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + // debug stuff, we just try to find the referencing question and don't do much with it + while (refq) + { + if (q == &refq->nta->question) + { + LogInfo("mDNSCoreRestartAddressQueries: Question %p %##s (%s) referring to GetZoneDataQuestion %p, not stopping", refq, refq->qname.c, DNSTypeName(refq->qtype), q); + } + refq = refq->next; + } + continue; + } + + // This function is called when /etc/hosts changes and that could affect A, AAAA and CNAME queries + if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA && q->qtype != kDNSType_CNAME) continue; + + // If the search domains did not change, then we restart all the queries. Otherwise, only + // for queries for which we "might" have appended search domains ("might" because we may + // find results before we apply search domains even though AppendSearchDomains is set to 1) + if (!SearchDomainsChanged || q->AppendSearchDomains) + { + // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero + // LOAddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before + // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers). Let us say that + // /etc/hosts has an A Record for web.apple.com. Any queries for web.apple.com will be answered locally. + // But this can't prevent a CNAME/AAAA query to not to be sent on the wire. When it is sent on the wire, + // it could create cache entries. When we are restarting queries, we can't deliver the cache RMV events + // for the original query using these cache entries as ADDs were never delivered using these cache + // entries and hence this order is needed. + + // If the query is suppressed, the RMV events won't be delivered + if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Cache Record RMV events"); continue; } + + // SuppressQuery status does not affect questions that are answered using local records + if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Local Record RMV events"); continue; } + + LogInfo("mDNSCoreRestartAddressQueries: Stop question %p %##s (%s), AppendSearchDomains %d, qnameOrig %p", q, + q->qname.c, DNSTypeName(q->qtype), q->AppendSearchDomains, q->qnameOrig); + mDNS_StopQuery_internal(m, q); + // Reset state so that it looks like it was in the beginning i.e it should look at /etc/hosts, cache + // and then search domains should be appended. At the beginning, qnameOrig was NULL. + if (q->qnameOrig) + { + LogInfo("mDNSCoreRestartAddressQueries: qnameOrig %##s", q->qnameOrig); + AssignDomainName(&q->qname, q->qnameOrig); + mDNSPlatformMemFree(q->qnameOrig); + q->qnameOrig = mDNSNULL; + q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; + } + q->SearchListIndex = 0; + q->next = restart; + restart = q; + } + } + + // 3. Callback before we start the query + if (BeforeStartCallback) BeforeStartCallback(m, context); + + // 4. Restart all the stopped queries + while (restart) + { + q = restart; + restart = restart->next; + q->next = mDNSNULL; + LogInfo("mDNSCoreRestartAddressQueries: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StartQuery_internal(m, q); + } +} + +mDNSexport void mDNSCoreRestartQueries(mDNS *const m) +{ + DNSQuestion *q; - 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 + // Retrigger all our uDNS questions + if (m->CurrentQuestion) + LogMsg("mDNSCoreRestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion) + { + q = m->CurrentQuestion; + m->CurrentQuestion = m->CurrentQuestion->next; + if (!mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) ActivateUnicastQuery(m, q, mDNStrue); + } +#endif + + // Retrigger all our mDNS questions + for (q = m->Questions; q; q=q->next) // Scan our list of questions + mDNSCoreRestartQuestion(m, q); +} + +// restart question if it's multicast and currently active +mDNSexport void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q) +{ + if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) + { + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question +#if mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; +#else // mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = SET_QU_IN_FIRST_QUERY; +#endif // mDNS_REQUEST_UNICAST_RESPONSE + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswerPkts = 0; + ExpireDupSuppressInfo(q->DupSuppress, m->timenow); + m->NextScheduledQuery = m->timenow; + } +} + +// restart the probe/announce cycle for multicast record +mDNSexport void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount) +{ + if (!AuthRecord_uDNS(rr)) + { + if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + + // announceCount < 0 indicates default announce count should be used + if (announceCount < 0) + announceCount = InitialAnnounceCount; + if (rr->AnnounceCount < announceCount) + rr->AnnounceCount = announceCount; + + if (mDNS_KeepaliveRecord(&rr->resrec)) + rr->AnnounceCount = 0; // Do not announce keepalive records + else + rr->AnnounceCount = InitialAnnounceCount; + rr->SendNSECNow = mDNSNULL; + InitializeLastAPTime(m, rr); + } +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Power Management (Sleep/Wake) +#endif + +mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) +{ +#ifndef IDLESLEEPCONTROL_DISABLED + mDNSBool allowSleep = mDNStrue; + char reason[128]; + + reason[0] = 0; + + if (m->SystemSleepOnlyIfWakeOnLAN) + { + // Don't sleep if we are a proxy for any services + if (m->ProxyRecords) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "sleep proxy for %d records", m->ProxyRecords); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because we are proxying %d records", m->ProxyRecords); + } + + if (allowSleep && mDNSCoreHaveAdvertisedMulticastServices(m)) + { + // Scan the list of active interfaces + NetworkInterfaceInfo *intf; + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + if (intf->McastTxRx && !intf->Loopback && !mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) + { + // Disallow sleep if this interface doesn't support NetWake + if (!intf->NetWake) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s does not support NetWake", intf->ifname); + break; + } + + // Disallow sleep if there is no sleep proxy server + const CacheRecord *cr = FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL); + if ( cr == mDNSNULL) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server on %s", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server", intf->ifname); + break; + } + else if (m->SPSType != 0) + { + mDNSu32 mymetric = LocalSPSMetric(m); + mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); + if (metric >= mymetric) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server with better metric on %s", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server with a better metric", intf->ifname); + break; + } + } + } + } + } + } + + // Call the platform code to enable/disable sleep + mDNSPlatformSetAllowSleep(m, allowSleep, reason); +#else + (void) m; +#endif /* !defined(IDLESLEEPCONTROL_DISABLED) */ +} + +mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSu32 scopeid) +{ + // If it is not a uDNS record, check to see if the updateid is zero. "updateid" is cleared when we have + // sent the resource record on all the interfaces. If the update id is not zero, check to see if it is time + // to send. + if (AuthRecord_uDNS(rr) || (rr->AuthFlags & AuthFlagsWakeOnly) || mDNSOpaque16IsZero(rr->updateid) || + m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) + { + return mDNSfalse; + } + + // If we have a pending registration for "scopeid", it is ok to send the update on that interface. + // If the scopeid is too big to check for validity, we don't check against updateIntID. When + // we successfully update on all the interfaces (with whatever set in "rr->updateIntID"), we clear + // updateid and we should have returned from above. + // + // Note: scopeid is the same as intf->InterfaceID. It is passed in so that we don't have to call the + // platform function to extract the value from "intf" everytime. + + if ((scopeid >= (sizeof(rr->updateIntID) * mDNSNBBY) || bit_get_opaque64(rr->updateIntID, scopeid)) && + (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == intf->InterfaceID)) + return mDNStrue; + + return mDNSfalse; +} + +mDNSexport void UpdateRMACCallback(mDNS *const m, void *context) +{ + IPAddressMACMapping *addrmap = (IPAddressMACMapping *)context ; + m->CurrentRecord = m->ResourceRecords; + + if (!addrmap) + { + LogMsg("UpdateRMACCallback: Address mapping is NULL"); + return; + } + + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + // If this is a keepalive record and the remote IP address matches, update the RData + if (mDNS_KeepaliveRecord(&rr->resrec)) + { + mDNSAddr raddr; + getKeepaliveRaddr(m, rr, &raddr); + if (mDNSSameAddress(&raddr, &addrmap->ipaddr)) + { + UpdateKeepaliveRData(m, rr, mDNSNULL, mDNStrue, (char *)(addrmap->ethaddr)); + } + } + m->CurrentRecord = rr->next; + } + + if (addrmap) + { + mDNSPlatformMemFree(addrmap); + } +} + +mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr) +{ + mDNSu16 newrdlength; + mDNSAddr laddr, raddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; + UTF8str255 txt; + int rdsize; + RData *newrd; + mDNSTCPInfo mti; + mStatus ret; + + // Note: If we fail to update the DNS NULL record with additional information in this function, it will be registered + // with the SPS like any other record. SPS will not send keepalives if it does not have additional information. + mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) || + mDNSIPPortIsZero(rport)) + { + LogMsg("UpdateKeepaliveRData: not a valid record %s for keepalive %#a:%d %#a:%d", ARDisplayString(m, rr), &laddr, lport.NotAnInteger, &raddr, rport.NotAnInteger); + return mStatus_UnknownErr; + } + + if (updateMac) + { + if (laddr.type == mDNSAddrType_IPv4) + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d h=%#a d=%#a l=%u r=%u m=%s", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ethAddr); + else + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d H=%#a D=%#a l=%u r=%u m=%s", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ethAddr); + + } + else + { + // If this keepalive packet would be sent on a different interface than the current one that we are processing + // now, then we don't update the DNS NULL record. But we do not prevent it from registering with the SPS. When SPS sees + // this DNS NULL record, it does not send any keepalives as it does not have all the information + mDNSPlatformMemZero(&mti, sizeof (mDNSTCPInfo)); + ret = mDNSPlatformRetrieveTCPInfo(m, &laddr, &lport, &raddr, &rport, &mti); + if (ret != mStatus_NoError) + { + LogMsg("mDNSPlatformRetrieveTCPInfo: mDNSPlatformRetrieveTCPInfo failed %d", ret); + return ret; + } + if ((intf != mDNSNULL) && (mti.IntfId != intf->InterfaceID)) + { + LogInfo("mDNSPlatformRetrieveTCPInfo: InterfaceID mismatch mti.IntfId = %p InterfaceID = %p", mti.IntfId, intf->InterfaceID); + return mStatus_BadParamErr; + } + + if (laddr.type == mDNSAddrType_IPv4) + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d h=%#a d=%#a l=%u r=%u m=%.6a s=%u a=%u w=%u", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ð, mti.seq, mti.ack, mti.window); + else + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d H=%#a D=%#a l=%u r=%u m=%.6a s=%u a=%u w=%u", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ð, mti.seq, mti.ack, mti.window); + } + + // Did we insert a null byte at the end ? + if (newrdlength == (sizeof(txt.c) - 1)) + { + LogMsg("UpdateKeepaliveRData: could not allocate memory %s", ARDisplayString(m, rr)); + return mStatus_NoMemoryErr; + } + + // Include the length for the null byte at the end + txt.c[0] = newrdlength + 1; + // Account for the first length byte and the null byte at the end + newrdlength += 2; + + rdsize = newrdlength > sizeof(RDataBody) ? newrdlength : sizeof(RDataBody); + newrd = mDNSPlatformMemAllocate(sizeof(RData) - sizeof(RDataBody) + rdsize); + if (!newrd) { LogMsg("UpdateKeepaliveRData: ptr NULL"); return mStatus_NoMemoryErr; } + + newrd->MaxRDLength = (mDNSu16) rdsize; + mDNSPlatformMemCopy(&newrd->u, txt.c, newrdlength); + + // If we are updating the record for the first time, rdata points to rdatastorage as the rdata memory + // was allocated as part of the AuthRecord itself. We allocate memory when we update the AuthRecord. + // If the resource record has data that we allocated in a previous pass (to update MAC address), + // free that memory here before copying in the new data. + if ( rr->resrec.rdata != &rr->rdatastorage) + { + mDNSPlatformMemFree(rr->resrec.rdata); + LogSPS("UpdateKeepaliveRData: Freed allocated memory for keep alive packet: %s ", ARDisplayString(m, rr)); + } + SetNewRData(&rr->resrec, newrd, newrdlength); // Update our rdata + + LogSPS("UpdateKeepaliveRData: successfully updated the record %s", ARDisplayString(m, rr)); + return mStatus_NoError; +} + +mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id, const OwnerOptData *const owner) +{ + const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC); + const int sps = intf->NextSPSAttempt / 3; + AuthRecord *rr; + mDNSOpaque16 msgid; + mDNSu32 scopeid; + + scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue); + if (!intf->SPSAddr[sps].type) + { + intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond; + if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0) + m->NextScheduledSPRetry = intf->NextSPSAttemptTime; + LogSPS("SendSPSRegistration: %s SPS %d (%d) %##s not yet resolved", intf->ifname, intf->NextSPSAttempt, sps, intf->NetWakeResolve[sps].qname.c); + goto exit; + } + + // Mark our mDNS records (not unicast records) for transfer to SPS + if (mDNSOpaque16IsZero(id)) + { + // We may have to register this record over multiple interfaces and we don't want to + // overwrite the id. We send the registration over interface X with id "IDX" and before + // we get a response, we overwrite with id "IDY" for interface Y and we won't accept responses + // for "IDX". Hence, we want to use the same ID across all interfaces. + // + // In the case of sleep proxy server transfering its records when it goes to sleep, the owner + // option check below will set the same ID across the records from the same owner. Records + // with different owner option gets different ID. + msgid = mDNS_NewMessageID(m); + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { + if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name)))) + { + if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner))) + { + rr->SendRNow = mDNSInterfaceMark; // mark it now + // When we are registering on the first interface, rr->updateid is zero in which case + // initialize with the new ID. For subsequent interfaces, we want to use the same ID. + // At the end, all the updates sent across all the interfaces with the same ID. + if (mDNSOpaque16IsZero(rr->updateid)) + rr->updateid = msgid; + else + msgid = rr->updateid; + } + } + } + } + } + else + msgid = id; + + while (1) + { + mDNSu8 *p = m->omsg.data; + // To comply with RFC 2782, PutResourceRecord suppresses name compression for SRV records in unicast updates. + // For now we follow that same logic for SPS registrations too. + // If we decide to compress SRV records in SPS registrations in the future, we can achieve that by creating our + // initial DNSMessage with h.flags set to zero, and then update it to UpdateReqFlags right before sending the packet. + InitializeDNSMessage(&m->omsg.h, msgid, UpdateReqFlags); + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->SendRNow || mDNSUpdateOkToSend(m, rr, intf, scopeid)) + { + if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner))) + { + mDNSu8 *newptr; + const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace; + + // If we can't update the keepalive record, don't send it + if (mDNS_KeepaliveRecord(&rr->resrec) && (UpdateKeepaliveRData(m, rr, intf, mDNSfalse, mDNSNULL) != mStatus_NoError)) + { + if (scopeid < (sizeof(rr->updateIntID) * mDNSNBBY)) + { + bit_clr_opaque64(rr->updateIntID, scopeid); + } + rr->SendRNow = mDNSNULL; + continue; + } + + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it + newptr = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state + if (!newptr) + LogSPS("SendSPSRegistration put %s FAILED %d/%d %s", intf->ifname, p - m->omsg.data, limit - m->omsg.data, ARDisplayString(m, rr)); + else + { + LogSPS("SendSPSRegistration put %s 0x%x 0x%x (updateid %d) %s", intf->ifname, rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(m->omsg.h.id), ARDisplayString(m, rr)); + rr->SendRNow = mDNSNULL; + rr->ThisAPInterval = mDNSPlatformOneSecond; + rr->LastAPTime = m->timenow; + // should be initialized above + if (mDNSOpaque16IsZero(rr->updateid)) LogMsg("SendSPSRegistration: ERROR!! rr %s updateid is zero", ARDisplayString(m, rr)); + if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); + p = newptr; + } + } + } + + if (!m->omsg.h.mDNS_numUpdates) break; + else + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT) * 2; // Two options in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT) * 2; + opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + opt.resrec.rdata->u.opt[0].optlen = DNSOpt_LeaseData_Space - 4; + opt.resrec.rdata->u.opt[0].u.updatelease = DEFAULT_UPDATE_LEASE; + if (!owner->HMAC.l[0]) // If no owner data, + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[1]); // use our own interface information + else // otherwise, use the owner data we were given + { + opt.resrec.rdata->u.opt[1].u.owner = *owner; + opt.resrec.rdata->u.opt[1].opt = kDNSOpt_Owner; + opt.resrec.rdata->u.opt[1].optlen = DNSOpt_Owner_Space(&owner->HMAC, &owner->IMAC) - 4; + } + LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, &opt)); + p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); + if (!p) + LogMsg("SendSPSRegistration: Failed to put OPT record (%d updates) %s", m->omsg.h.mDNS_numUpdates, ARDisplayString(m, &opt)); + else + { + mStatus err; + + LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, + mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); + // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss + err = mDNSSendDNSMessage(m, &m->omsg, p, intf->InterfaceID, mDNSNULL, &intf->SPSAddr[sps], intf->SPSPort[sps], mDNSNULL, mDNSNULL, mDNSfalse); + if (err) LogSPS("SendSPSRegistration: mDNSSendDNSMessage err %d", err); + if (err && intf->SPSAddr[sps].type == mDNSAddrType_IPv4 && intf->NetWakeResolve[sps].ThisQInterval == -1) + { + LogSPS("SendSPSRegistration %d %##s failed to send to IPv4 address; will try IPv6 instead", sps, intf->NetWakeResolve[sps].qname.c); + intf->NetWakeResolve[sps].qtype = kDNSType_AAAA; + mDNS_StartQuery_internal(m, &intf->NetWakeResolve[sps]); + return; + } + } + } + } + + intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond * 10; // If successful, update NextSPSAttemptTime +exit: + if (mDNSOpaque16IsZero(id) && intf->NextSPSAttempt < 8) intf->NextSPSAttempt++; +} + +mDNSlocal mDNSBool RecordIsFirstOccurrenceOfOwner(mDNS *const m, const AuthRecord *const rr) +{ + AuthRecord *ar; + for (ar = m->ResourceRecords; ar && ar != rr; ar=ar->next) + if (mDNSPlatformMemSame(&rr->WakeUp, &ar->WakeUp, sizeof(rr->WakeUp))) return mDNSfalse; + return mDNStrue; +} + +mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID InterfaceID, AuthRecord *const rr) +{ + AuthRecord *newRR = mDNSPlatformMemAllocate(sizeof(AuthRecord)); + + if (newRR == mDNSNULL) + { + LogSPS("%s : could not allocate memory for new resource record", __func__); + return; + } + + mDNSPlatformMemZero(newRR, sizeof(AuthRecord)); + mDNS_SetupResourceRecord(newRR, mDNSNULL, InterfaceID, rr->resrec.rrtype, + rr->resrec.rroriginalttl, rr->resrec.RecordType, + rr->ARType, mDNSNULL, mDNSNULL); + + AssignDomainName(&newRR->namestorage, &rr->namestorage); + newRR->resrec.rdlength = DomainNameLength(rr->resrec.name); + newRR->resrec.namehash = DomainNameHashValue(newRR->resrec.name); + newRR->resrec.rrclass = rr->resrec.rrclass; + + if (rr->resrec.rrtype == kDNSType_A) + { + newRR->resrec.rdata->u.ipv4 = rr->resrec.rdata->u.ipv4; + } + else if (rr->resrec.rrtype == kDNSType_AAAA) + { + newRR->resrec.rdata->u.ipv6 = rr->resrec.rdata->u.ipv6; + } + SetNewRData(&newRR->resrec, mDNSNULL, 0); + + // Insert the new node at the head of the list. + newRR->next = m->SPSRRSet; + m->SPSRRSet = newRR; + LogSPS("%s : Storing proxy record : %s ", __func__, ARDisplayString(m, rr)); +} + +// Some records are interface specific and some are not. The ones that are supposed to be registered +// on multiple interfaces need to be initialized with all the valid interfaces on which it will be sent. +// updateIntID bit field tells us on which interfaces we need to register this record. When we get an +// ack from the sleep proxy server, we clear the interface bit. This way, we know when a record completes +// registration on all the interfaces +mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntID, mDNSBool *WakeOnlyService) +{ + AuthRecord *ar; + LogSPS("SPSInitRecordsBeforeUpdate: UpdateIntID 0x%x 0x%x", updateIntID.l[1], updateIntID.l[0]); + + *WakeOnlyService = mDNSfalse; + + // Before we store the A and AAAA records that we are going to register with the sleep proxy, + // make sure that the old sleep proxy records are removed. + mDNSCoreFreeProxyRR(m); + + // For records that are registered only on a specific interface, mark only that bit as it will + // never be registered on any other interface. For others, it should be sent on all interfaces. + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + ar->updateIntID = zeroOpaque64; + ar->updateid = zeroID; + if (AuthRecord_uDNS(ar)) + { + continue; + } + if (ar->AuthFlags & AuthFlagsWakeOnly) + { + if (ar->resrec.RecordType == kDNSRecordTypeShared && ar->RequireGoodbye) + { + ar->ImmedAnswer = mDNSInterfaceMark; + *WakeOnlyService = mDNStrue; + continue; + } + } + if (!ar->resrec.InterfaceID) + { + LogSPS("Setting scopeid (ALL) 0x%x 0x%x for %s", updateIntID.l[1], updateIntID.l[0], ARDisplayString(m, ar)); + ar->updateIntID = updateIntID; + } + else + { + // Filter records that belong to interfaces that we won't register the records on. UpdateIntID captures + // exactly this. + mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, ar->resrec.InterfaceID, mDNStrue); + if ((scopeid < (sizeof(updateIntID) * mDNSNBBY)) && bit_get_opaque64(updateIntID, scopeid)) + { + bit_set_opaque64(ar->updateIntID, scopeid); + LogSPS("SPSInitRecordsBeforeUpdate: Setting scopeid(%d) 0x%x 0x%x for %s", scopeid, ar->updateIntID.l[1], + ar->updateIntID.l[0], ARDisplayString(m, ar)); + } + else + { + LogSPS("SPSInitRecordsBeforeUpdate: scopeid %d beyond range or not valid for SPS registration", scopeid); + } + } + // Store the A and AAAA records that we registered with the sleep proxy. + // We will use this to prevent spurious name conflicts that may occur when we wake up + if (ar->resrec.rrtype == kDNSType_A || ar->resrec.rrtype == kDNSType_AAAA) + { + mDNSCoreStoreProxyRR(m, ar->resrec.InterfaceID, ar); + } + } +} + +mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id) +{ + AuthRecord *ar; + OwnerOptData owner = zeroOwner; + + SendSPSRegistrationForOwner(m, intf, id, &owner); + + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)) && RecordIsFirstOccurrenceOfOwner(m, ar)) + { + owner = ar->WakeUp; + SendSPSRegistrationForOwner(m, intf, id, &owner); + } + } +} + +// RetrySPSRegistrations is called from SendResponses, with the lock held +mDNSlocal void RetrySPSRegistrations(mDNS *const m) +{ + AuthRecord *rr; + NetworkInterfaceInfo *intf; + + // First make sure none of our interfaces' NextSPSAttemptTimes are inadvertently set to m->timenow + mDNSPlatformOneSecond * 10 + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NextSPSAttempt && intf->NextSPSAttemptTime == m->timenow + mDNSPlatformOneSecond * 10) + intf->NextSPSAttemptTime++; + + // Retry any record registrations that are due + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (!AuthRecord_uDNS(rr) && !mDNSOpaque16IsZero(rr->updateid) && m->timenow - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + { + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + // If we still have registrations pending on this interface, send it now + mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue); + if ((scopeid >= (sizeof(rr->updateIntID) * mDNSNBBY) || bit_get_opaque64(rr->updateIntID, scopeid)) && + (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == intf->InterfaceID)) + { + LogSPS("RetrySPSRegistrations: 0x%x 0x%x (updateid %d) %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m, rr)); + SendSPSRegistration(m, intf, rr->updateid); + } + } + } + + // For interfaces where we did an SPS registration attempt, increment intf->NextSPSAttempt + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NextSPSAttempt && intf->NextSPSAttemptTime == m->timenow + mDNSPlatformOneSecond * 10 && intf->NextSPSAttempt < 8) + intf->NextSPSAttempt++; +} + +mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + NetworkInterfaceInfo *intf = (NetworkInterfaceInfo *)question->QuestionContext; + int sps = (int)(question - intf->NetWakeResolve); + (void)m; // Unused + LogSPS("NetWakeResolve: SPS: %d Add: %d %s", sps, AddRecord, RRDisplayString(m, answer)); + + if (!AddRecord) return; // Don't care about REMOVE events + if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs + + // if (answer->rrtype == kDNSType_AAAA && sps == 0) return; // To test failing to resolve sleep proxy's address + + if (answer->rrtype == kDNSType_SRV) + { + // 1. Got the SRV record; now look up the target host's IP address + mDNS_StopQuery(m, question); + intf->SPSPort[sps] = answer->rdata->u.srv.port; + AssignDomainName(&question->qname, &answer->rdata->u.srv.target); + question->qtype = kDNSType_A; + mDNS_StartQuery(m, question); + } + else if (answer->rrtype == kDNSType_A && answer->rdlength == sizeof(mDNSv4Addr)) + { + // 2. Got an IPv4 address for the target host; record address and initiate an SPS registration if appropriate + mDNS_StopQuery(m, question); + question->ThisQInterval = -1; + intf->SPSAddr[sps].type = mDNSAddrType_IPv4; + intf->SPSAddr[sps].ip.v4 = answer->rdata->u.ipv4; + mDNS_Lock(m); + if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID); // If we're ready for this result, use it now + mDNS_Unlock(m); + } + else if (answer->rrtype == kDNSType_A && answer->rdlength == 0) + { + // 3. Got negative response -- target host apparently has IPv6 disabled -- so try looking up the target host's IPv4 address(es) instead + mDNS_StopQuery(m, question); + LogSPS("NetWakeResolve: SPS %d %##s has no IPv4 address, will try IPv6 instead", sps, question->qname.c); + question->qtype = kDNSType_AAAA; + mDNS_StartQuery(m, question); + } + else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == sizeof(mDNSv6Addr) && mDNSv6AddressIsLinkLocal(&answer->rdata->u.ipv6)) + { + // 4. Got the target host's IPv6 link-local address; record address and initiate an SPS registration if appropriate + mDNS_StopQuery(m, question); + question->ThisQInterval = -1; + intf->SPSAddr[sps].type = mDNSAddrType_IPv6; + intf->SPSAddr[sps].ip.v6 = answer->rdata->u.ipv6; + mDNS_Lock(m); + if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID); // If we're ready for this result, use it now + mDNS_Unlock(m); + } +} + +mDNSexport mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m) +{ + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (mDNS_KeepaliveRecord(&rr->resrec) || (rr->resrec.rrtype == kDNSType_SRV && !AuthRecord_uDNS(rr) && !mDNSSameIPPort(rr->resrec.rdata->u.srv.port, DiscardPort))) + return mDNStrue; + return mDNSfalse; +} + +#ifdef APPLE_OSX_mDNSResponder +// This function is used only in the case of local NIC proxy. For external +// sleep proxy server, we do this in SPSInitRecordsBeforeUpdate when we +// walk the resource records. +mDNSlocal void SendGoodbyesForWakeOnlyService(mDNS *const m, mDNSBool *WakeOnlyService) +{ + AuthRecord *rr; + + *WakeOnlyService = mDNSfalse; + + // Mark all the records we need to deregister and send them + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if ((rr->AuthFlags & AuthFlagsWakeOnly) && + rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + { + rr->ImmedAnswer = mDNSInterfaceMark; + *WakeOnlyService = mDNStrue; + } + } +} +#endif // APPLE_OSx_mDNSResponder + +mDNSlocal void SendSleepGoodbyes(mDNS *const m, mDNSBool AllInterfaces, mDNSBool unicast) +{ + AuthRecord *rr; + m->SleepState = SleepState_Sleeping; + + // If AllInterfaces is not set, the caller has already marked it appropriately + // on which interfaces this should be sent. + if (AllInterfaces) + { + NetworkInterfaceInfo *intf; + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + intf->SendGoodbyes = 1; + } + } + if (unicast) + { #ifndef UNICAST_DISABLED - uDNS_Execute(m); + SleepRecordRegistrations(m); // If we have no SPS, need to deregister our uDNS records +#endif /* UNICAST_DISABLED */ + } + + // Mark all the records we need to deregister and send them + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + rr->ImmedAnswer = mDNSInterfaceMark; + SendResponses(m); +} + +/* + * This function attempts to detect if multiple interfaces are on the same subnet. + * It makes this determination based only on the IPv4 Addresses and subnet masks. + * IPv6 link local addresses that are configured by default on all interfaces make + * it hard to make this determination + * + * The 'real' fix for this would be to send out multicast packets over one interface + * and conclude that multiple interfaces are on the same subnet only if these packets + * are seen on other interfaces on the same system + */ +mDNSlocal mDNSBool skipSameSubnetRegistration(mDNS *const m, mDNSInterfaceID *regID, mDNSu32 count, mDNSInterfaceID intfid) +{ + NetworkInterfaceInfo *intf; + NetworkInterfaceInfo *newIntf; + mDNSu32 i; + + for (newIntf = FirstInterfaceForID(m, intfid); newIntf; newIntf = newIntf->next) + { + if ((newIntf->InterfaceID != intfid) || + (newIntf->ip.type != mDNSAddrType_IPv4)) + { + continue; + } + for ( i = 0; i < count; i++) + { + for (intf = FirstInterfaceForID(m, regID[i]); intf; intf = intf->next) + { + if ((intf->InterfaceID != regID[i]) || + (intf->ip.type != mDNSAddrType_IPv4)) + { + continue; + } + if ((intf->ip.ip.v4.NotAnInteger & intf->mask.ip.v4.NotAnInteger) == (newIntf->ip.ip.v4.NotAnInteger & newIntf->mask.ip.v4.NotAnInteger)) + { + LogSPS("%s : Already registered for the same subnet (IPv4) for interface %s", __func__, intf->ifname); + return (mDNStrue); + } + } + } + } + return (mDNSfalse); +} + +mDNSlocal void DoKeepaliveCallbacks(mDNS *m) +{ + // Loop through the keepalive records and callback with an error + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if ((mDNS_KeepaliveRecord(&rr->resrec)) && (rr->resrec.RecordType != kDNSRecordTypeDeregistering)) + { + LogSPS("DoKeepaliveCallbacks: Invoking the callback for %s", ARDisplayString(m, rr)); + if (rr->RecordCallback) + rr->RecordCallback(m, rr, mStatus_BadStateErr); + } + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} + +// BeginSleepProcessing is called, with the lock held, from either mDNS_Execute or mDNSCoreMachineSleep +mDNSlocal void BeginSleepProcessing(mDNS *const m) +{ + mDNSBool SendGoodbyes = mDNStrue; + mDNSBool WakeOnlyService = mDNSfalse; + mDNSBool invokeKACallback = mDNStrue; + const CacheRecord *sps[3] = { mDNSNULL }; + mDNSOpaque64 updateIntID = zeroOpaque64; + mDNSInterfaceID registeredIntfIDS[128]; + mDNSu32 registeredCount = 0; + int skippedRegistrations = 0; + + m->NextScheduledSPRetry = m->timenow; + + if (!m->SystemWakeOnLANEnabled) LogSPS("BeginSleepProcessing: m->SystemWakeOnLANEnabled is false"); + else if (!mDNSCoreHaveAdvertisedMulticastServices(m)) LogSPS("BeginSleepProcessing: No advertised services"); + else // If we have at least one advertised service + { + NetworkInterfaceInfo *intf; + + // Clear out the SCDynamic entry that stores the external SPS information + mDNSPlatformClearSPSMACAddr(); + + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + // Intialize it to false. These values make sense only when SleepState is set to Sleeping. + intf->SendGoodbyes = 0; + + // If it is not multicast capable, we could not have possibly discovered sleep proxy + // servers. + if (!intf->McastTxRx || mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) + { + LogSPS("BeginSleepProcessing: %-6s Ignoring for registrations", intf->ifname); + continue; + } + + // If we are not capable of WOMP, then don't register with sleep proxy. + // + // Note: If we are not NetWake capable, we don't browse for the sleep proxy server. + // We might find sleep proxy servers in the cache and start a resolve on them. + // But then if the interface goes away, we won't stop these questions because + // mDNS_DeactivateNetWake_internal assumes that a browse has been started for it + // to stop both the browse and resolve questions. + if (!intf->NetWake) + { + LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname); + intf->SendGoodbyes = 1; + skippedRegistrations++; + continue; + } + + // Check if we have already registered with a sleep proxy for this subnet + if (skipSameSubnetRegistration(m, registeredIntfIDS, registeredCount, intf->InterfaceID)) + { + LogSPS("%s : Skipping sleep proxy registration on %s", __func__, intf->ifname); + continue; + } + +#if APPLE_OSX_mDNSResponder + else if (SupportsInNICProxy(intf)) + { + if (ActivateLocalProxy(m, intf) == mStatus_NoError) + { + SendGoodbyesForWakeOnlyService(m, &WakeOnlyService); + SendGoodbyes = mDNSfalse; + invokeKACallback = mDNSfalse; + LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname); + // This will leave m->SleepState set to SleepState_Transferring, + // which is okay because with no outstanding resolves, or updates in flight, + // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed + + registeredIntfIDS[registeredCount] = intf->InterfaceID; + registeredCount++; + } + } +#endif // APPLE_OSX_mDNSResponder + else + { +#if APPLE_OSX_mDNSResponder + // If on battery, do not attempt to offload to external sleep proxies + if (m->SystemWakeOnLANEnabled == mDNS_WakeOnBattery) + { + LogSPS("BegingSleepProcessing: Not connected to AC power - Not registering with an external sleep proxy."); + return; + } +#endif // APPLE_OSX_mDNSResponder + FindSPSInCache(m, &intf->NetWakeBrowse, sps); + if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)", + intf->ifname, &intf->ip, NextQSendTime(&intf->NetWakeBrowse) - m->timenow, intf->NetWakeBrowse.ThisQInterval); + else + { + int i; + mDNSu32 scopeid; + SendGoodbyes = mDNSfalse; + intf->NextSPSAttempt = 0; + intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond; + + scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue); + // Now we know for sure that we have to wait for registration to complete on this interface. + if (scopeid < (sizeof(updateIntID) * mDNSNBBY)) + bit_set_opaque64(updateIntID, scopeid); + + // Don't need to set m->NextScheduledSPRetry here because we already set "m->NextScheduledSPRetry = m->timenow" above + for (i=0; i<3; i++) + { +#if ForceAlerts + if (intf->SPSAddr[i].type) + { LogMsg("BeginSleepProcessing: %s %d intf->SPSAddr[i].type %d", intf->ifname, i, intf->SPSAddr[i].type); *(long*)0 = 0; } + if (intf->NetWakeResolve[i].ThisQInterval >= 0) + { LogMsg("BeginSleepProcessing: %s %d intf->NetWakeResolve[i].ThisQInterval %d", intf->ifname, i, intf->NetWakeResolve[i].ThisQInterval); *(long*)0 = 0; } #endif - mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value - return(m->NextScheduledEvent); - } + intf->SPSAddr[i].type = mDNSAddrType_None; + if (intf->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery(m, &intf->NetWakeResolve[i]); + intf->NetWakeResolve[i].ThisQInterval = -1; + if (sps[i]) + { + LogSPS("BeginSleepProcessing: %-6s Found Sleep Proxy Server %d TTL %d %s", intf->ifname, i, sps[i]->resrec.rroriginalttl, CRDisplayString(m, sps[i])); + mDNS_SetupQuestion(&intf->NetWakeResolve[i], intf->InterfaceID, &sps[i]->resrec.rdata->u.name, kDNSType_SRV, NetWakeResolve, intf); + intf->NetWakeResolve[i].ReturnIntermed = mDNStrue; + mDNS_StartQuery_internal(m, &intf->NetWakeResolve[i]); + + // If we are registering with a Sleep Proxy for a new subnet, add it to our list + registeredIntfIDS[registeredCount] = intf->InterfaceID; + registeredCount++; + } + } + } + } + } + } + + // If we have at least one interface on which we are registering with an external sleep proxy, + // initialize all the records appropriately. + if (!mDNSOpaque64IsZero(&updateIntID)) + SPSInitRecordsBeforeUpdate(m, updateIntID, &WakeOnlyService); + + // Call the applicaitons that registered a keepalive record to inform them that we failed to offload + // the records to a sleep proxy. + if (invokeKACallback) + { + LogSPS("BeginSleepProcessing: Did not register with an in-NIC proxy - invoking the callbacks for KA records"); + DoKeepaliveCallbacks(m); + } + + // SendSleepGoodbyes last two arguments control whether we send goodbyes on all + // interfaces and also deregister unicast registrations. + // + // - If there are no sleep proxy servers, then send goodbyes on all interfaces + // for both multicast and unicast. + // + // - If we skipped registrations on some interfaces, then we have already marked + // them appropriately above. We don't need to send goodbyes for unicast as + // we have registered with at least one sleep proxy. + // + // - If we are not planning to send any goodbyes, then check for WakeOnlyServices. + // + // Note: If we are planning to send goodbyes, we mark the record with mDNSInterfaceAny + // and call SendResponses which inturn calls ShouldSendGoodbyesBeforeSleep which looks + // at WakeOnlyServices first. + if (SendGoodbyes) + { + LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server"); + SendSleepGoodbyes(m, mDNStrue, mDNStrue); + } + else if (skippedRegistrations) + { + LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server on all interfaces"); + SendSleepGoodbyes(m, mDNSfalse, mDNSfalse); + } + else if (WakeOnlyService) + { + // If we saw WakeOnly service above, send the goodbyes now. + LogSPS("BeginSleepProcessing: Sending goodbyes for WakeOnlyServices"); + SendResponses(m); + } +} // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep. // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up. // Normally, the platform support layer below mDNSCore should call this, not the client layer above. -// 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; +mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) +{ + AuthRecord *rr; + + LogSPS("%s (old state %d) at %ld", sleep ? "Sleeping" : "Waking", m->SleepState, m->timenow); + + if (sleep && !m->SleepState) // Going to sleep + { + mDNS_Lock(m); + // If we're going to sleep, need to stop advertising that we're a Sleep Proxy Server + if (m->SPSSocket) + { + mDNSu8 oldstate = m->SPSState; + mDNS_DropLockBeforeCallback(); // mDNS_DeregisterService expects to be called without the lock held, so we emulate that here + m->SPSState = 2; +#ifndef SPC_DISABLED + if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords); +#else + (void)oldstate; +#endif + mDNS_ReclaimLockAfterCallback(); + } + + m->SleepState = SleepState_Transferring; + if (m->SystemWakeOnLANEnabled && m->DelaySleep) + { + // If we just woke up moments ago, allow ten seconds for networking to stabilize before going back to sleep + LogSPS("mDNSCoreMachineSleep: Re-sleeping immediately after waking; will delay for %d ticks", m->DelaySleep - m->timenow); + m->SleepLimit = NonZeroTime(m->DelaySleep + mDNSPlatformOneSecond * 10); + } + else + { + m->DelaySleep = 0; + m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10); + m->mDNSStats.Sleeps++; + BeginSleepProcessing(m); + } #ifndef UNICAST_DISABLED - uDNS_Wake(m); + SuspendLLQs(m); +#endif +#if APPLE_OSX_mDNSResponder + RemoveAutoTunnel6Record(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); - } + LogSPS("mDNSCoreMachineSleep: m->SleepState %d (%s) seq %d", m->SleepState, + m->SleepState == SleepState_Transferring ? "Transferring" : + m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", m->SleepSeqNum); + mDNS_Unlock(m); + } + else if (!sleep) // Waking up + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + NetworkInterfaceInfo *intf; + mDNSs32 currtime, diff; + + mDNS_Lock(m); + // Reset SleepLimit back to 0 now that we're awake again. + m->SleepLimit = 0; + + // If we were previously sleeping, but now we're not, increment m->SleepSeqNum to indicate that we're entering a new period of wakefulness + if (m->SleepState != SleepState_Awake) + { + m->SleepState = SleepState_Awake; + m->SleepSeqNum++; + // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) + // then we enforce a minimum delay of 16 seconds before we begin sleep processing. + // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., + // before we make our determination of whether there's a Sleep Proxy out there we should register with. + m->DelaySleep = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 16); + } + + if (m->SPSState == 3) + { + m->SPSState = 0; + mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags); + } + m->mDNSStats.Wakes++; + + // ... and the same for NextSPSAttempt + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1; + + // Restart unicast and multicast queries + mDNSCoreRestartQueries(m); + + // and reactivtate service registrations + m->NextSRVUpdate = NonZeroTime(m->timenow + mDNSPlatformOneSecond); + LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + + // 2. Re-validate our cache records + currtime = mDNSPlatformUTC(); + +#if APPLE_OSX_mDNSResponder + // start time of this statistics gathering interval + m->StatStartTime = currtime; +#endif // APPLE_OSX_mDNSResponder + + diff = currtime - m->TimeSlept; + FORALL_CACHERECORDS(slot, cg, cr) + { + // Temporary fix: For unicast cache records, look at how much time we slept. + // Adjust the RecvTime by the amount of time we slept so that we age the + // cache record appropriately. If it is expired already, purge. If there + // is a network change that happens after the wakeup, we might purge the + // cache anyways and this helps only in the case where there are no network + // changes across sleep/wakeup transition. + // + // Note: If there is a network/DNS server change that already happened and + // these cache entries are already refreshed and we are getting a delayed + // wake up notification, we might adjust the TimeRcvd based on the time slept + // now which can cause the cache to purge pre-maturely. As this is not a very + // common case, this should happen rarely. + if (!cr->resrec.InterfaceID) + { + if (diff > 0) + { + mDNSu32 uTTL = RRUnadjustedTTL(cr->resrec.rroriginalttl); + const mDNSs32 remain = uTTL - (m->timenow - cr->TimeRcvd) / mDNSPlatformOneSecond; + + // -if we have slept longer than the remaining TTL, purge and start fresh. + // -if we have been sleeping for a long time, we could reduce TimeRcvd below by + // a sufficiently big value which could cause the value to go into the future + // because of the signed comparison of time. For this to happen, we should have been + // sleeping really long (~24 days). For now, we want to be conservative and flush even + // if we have slept for more than two days. + + if (diff >= remain || diff > (2 * 24 * 3600)) + { + LogInfo("mDNSCoreMachineSleep: %s: Purging cache entry SleptTime %d, Remaining TTL %d", + CRDisplayString(m, cr), diff, remain); + mDNS_PurgeCacheResourceRecord(m, cr); + continue; + } + cr->TimeRcvd -= (diff * mDNSPlatformOneSecond); + if (m->timenow - (cr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0) + { + LogInfo("mDNSCoreMachineSleep: %s: Purging after adjusting the remaining TTL %d by %d seconds", + CRDisplayString(m, cr), remain, diff); + mDNS_PurgeCacheResourceRecord(m, cr); + } + else + { + LogInfo("mDNSCoreMachineSleep: %s: Adjusted the remain ttl %u by %d seconds", CRDisplayString(m, cr), remain, diff); + } + } + } + else + { + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake); + } + } + + // 3. Retrigger probing and announcing for all our authoritative records + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (AuthRecord_uDNS(rr)) + { + ActivateUnicastRegistration(m, rr); + } + else + { + mDNSCoreRestartRegistration(m, rr, -1); + } + } + + // 4. Refresh NAT mappings + // We don't want to have to assume that all hardware can necessarily keep accurate + // track of passage of time while asleep, so on wake we refresh our NAT mappings. + // We typically wake up with no interfaces active, so there's no need to rush to try to find our external address. + // But if we do get a network configuration change, mDNSMacOSXNetworkChanged will call uDNS_SetupDNSConfig, which + // will call mDNS_SetPrimaryInterfaceInfo, which will call RecreateNATMappings to refresh them, potentially sooner + // than five seconds from now. + LogInfo("mDNSCoreMachineSleep: recreating NAT mappings in 5 seconds"); + RecreateNATMappings(m, mDNSPlatformOneSecond * 5); + mDNS_Unlock(m); + } +} + +mDNSexport mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now) +{ + DNSQuestion *q; + AuthRecord *rr; + NetworkInterfaceInfo *intf; + + mDNS_Lock(m); + + if (m->DelaySleep) goto notready; + + // If we've not hit the sleep limit time, and it's not time for our next retry, we can skip these checks + if (m->SleepLimit - now > 0 && m->NextScheduledSPRetry - now > 0) goto notready; + + m->NextScheduledSPRetry = now + 0x40000000UL; + + // See if we might need to retransmit any lost Sleep Proxy Registrations + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NextSPSAttempt >= 0) + { + if (now - intf->NextSPSAttemptTime >= 0) + { + LogSPS("mDNSCoreReadyForSleep: retrying for %s SPS %d try %d", + intf->ifname, intf->NextSPSAttempt/3, intf->NextSPSAttempt); + SendSPSRegistration(m, intf, zeroID); + // Don't need to "goto notready" here, because if we do still have record registrations + // that have not been acknowledged yet, we'll catch that in the record list scan below. + } + else + if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0) + m->NextScheduledSPRetry = intf->NextSPSAttemptTime; + } + + // Scan list of interfaces, and see if we're still waiting for any sleep proxy resolves to complete + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + int sps = (intf->NextSPSAttempt == 0) ? 0 : (intf->NextSPSAttempt-1)/3; + if (intf->NetWakeResolve[sps].ThisQInterval >= 0) + { + LogSPS("mDNSCoreReadyForSleep: waiting for SPS Resolve %s %##s (%s)", + intf->ifname, intf->NetWakeResolve[sps].qname.c, DNSTypeName(intf->NetWakeResolve[sps].qtype)); + goto spsnotready; + } + } + + // Scan list of registered records + for (rr = m->ResourceRecords; rr; rr = rr->next) + if (!AuthRecord_uDNS(rr)) + if (!mDNSOpaque64IsZero(&rr->updateIntID)) + { LogSPS("mDNSCoreReadyForSleep: waiting for SPS updateIntID 0x%x 0x%x (updateid %d) %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto spsnotready; } + + // Scan list of private LLQs, and make sure they've all completed their handshake with the server + for (q = m->Questions; q; q = q->next) + if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->ReqLease == 0 && q->tcp) + { + LogSPS("mDNSCoreReadyForSleep: waiting for LLQ %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + goto notready; + } + + // Scan list of registered records + for (rr = m->ResourceRecords; rr; rr = rr->next) + if (AuthRecord_uDNS(rr)) + { + if (rr->state == regState_Refresh && rr->tcp) + { LogSPS("mDNSCoreReadyForSleep: waiting for Record updateIntID 0x%x 0x%x (updateid %d) %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; } + #if APPLE_OSX_mDNSResponder + if (!RecordReadyForSleep(m, rr)) { LogSPS("mDNSCoreReadyForSleep: waiting for %s", ARDisplayString(m, rr)); goto notready; } + #endif + } + + mDNS_Unlock(m); + return mDNStrue; + +spsnotready: + + // If we failed to complete sleep proxy registration within ten seconds, we give up on that + // and allow up to ten seconds more to complete wide-area deregistration instead + if (now - m->SleepLimit >= 0) + { + LogMsg("Failed to register with SPS, now sending goodbyes"); + + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NetWakeBrowse.ThisQInterval >= 0) + { + LogSPS("ReadyForSleep mDNS_DeactivateNetWake %s %##s (%s)", + intf->ifname, intf->NetWakeResolve[0].qname.c, DNSTypeName(intf->NetWakeResolve[0].qtype)); + mDNS_DeactivateNetWake_internal(m, intf); + } + + for (rr = m->ResourceRecords; rr; rr = rr->next) + if (!AuthRecord_uDNS(rr)) + if (!mDNSOpaque64IsZero(&rr->updateIntID)) + { + LogSPS("ReadyForSleep clearing updateIntID 0x%x 0x%x (updateid %d) for %s", rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m, rr)); + rr->updateIntID = zeroOpaque64; + } + + // We'd really like to allow up to ten seconds more here, + // but if we don't respond to the sleep notification within 30 seconds + // we'll be put back to sleep forcibly without the chance to schedule the next maintenance wake. + // Right now we wait 16 sec after wake for all the interfaces to come up, then we wait up to 10 seconds + // more for SPS resolves and record registrations to complete, which puts us at 26 seconds. + // If we allow just one more second to send our goodbyes, that puts us at 27 seconds. + m->SleepLimit = now + mDNSPlatformOneSecond * 1; + + SendSleepGoodbyes(m, mDNStrue, mDNStrue); + } + +notready: + mDNS_Unlock(m); + return mDNSfalse; +} + +mDNSexport mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now) +{ + AuthRecord *ar; + + // Even when we have no wake-on-LAN-capable interfaces, or we failed to find a sleep proxy, or we have other + // failure scenarios, we still want to wake up in at most 120 minutes, to see if the network environment has changed. + // E.g. we might wake up and find no wireless network because the base station got rebooted just at that moment, + // and if that happens we don't want to just give up and go back to sleep and never try again. + mDNSs32 e = now + (120 * 60 * mDNSPlatformOneSecond); // Sleep for at most 120 minutes + + NATTraversalInfo *nat; + for (nat = m->NATTraversals; nat; nat=nat->next) + if (nat->Protocol && nat->ExpiryTime && nat->ExpiryTime - now > mDNSPlatformOneSecond*4) + { + mDNSs32 t = nat->ExpiryTime - (nat->ExpiryTime - now) / 10; // Wake up when 90% of the way to the expiry time + if (e - t > 0) e = t; + LogSPS("ComputeWakeTime: %p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d Wake %5d", + nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP", + mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result, + nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, + nat->retryInterval / mDNSPlatformOneSecond, + nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0, + (t - now) / mDNSPlatformOneSecond); + } + + // This loop checks both the time we need to renew wide-area registrations, + // and the time we need to renew Sleep Proxy registrations + for (ar = m->ResourceRecords; ar; ar = ar->next) + if (ar->expire && ar->expire - now > mDNSPlatformOneSecond*4) + { + mDNSs32 t = ar->expire - (ar->expire - now) / 10; // Wake up when 90% of the way to the expiry time + if (e - t > 0) e = t; + LogSPS("ComputeWakeTime: %p Int %7d Next %7d Expire %7d Wake %7d %s", + ar, ar->ThisAPInterval / mDNSPlatformOneSecond, + (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, + ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, + (t - now) / mDNSPlatformOneSecond, ARDisplayString(m, ar)); + } + + return(e - now); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -4684,94 +6763,96 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) #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); - } + const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords) +{ + mDNSu8 *responseptr = response->data; + const mDNSu8 *const limit = response->data + sizeof(response->data); + const mDNSu8 *ptr = query->data; + AuthRecord *rr; + mDNSu32 maxttl = 0x70000000; + int i; + + // Initialize the response fields so we can answer the questions + InitializeDNSMessage(&response->h, query->h.id, ResponseFlags); + + // *** + // *** 1. Write out the list of questions we are actually going to answer with this packet + // *** + if (LegacyQuery) + { + maxttl = kStaticCacheTTL; + for (i=0; i<query->h.numQuestions; i++) // For each question... + { + DNSQuestion q; + ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question... + if (!ptr) return(mDNSNULL); + + for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers + { + if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question + { // then put the question in the question section + responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass); + if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); } + break; // break out of the ResponseRecords loop, and go on to the next question + } + } + } + + if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); } + } + + // *** + // *** 2. Write Answers + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AnswerTo) + { + mDNSu8 *p = PutResourceRecordTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, + maxttl < rr->resrec.rroriginalttl ? maxttl : rr->resrec.rroriginalttl); + if (p) responseptr = p; + else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; } + } + + // *** + // *** 3. Write Additionals + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AdditionalTo && !rr->NR_AnswerTo) + { + mDNSu8 *p = PutResourceRecordTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, + maxttl < rr->resrec.rroriginalttl ? maxttl : rr->resrec.rroriginalttl); + if (p) responseptr = p; + else debugf("GenerateUnicastResponse: No more space for additionals"); + } + + return(responseptr); +} // AuthRecord *our is our Resource Record // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network // Returns 0 if there is no conflict // Returns +1 if there was a conflict and we won // Returns -1 if there was a conflict and we lost and have to rename -mDNSlocal int CompareRData(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); - } +mDNSlocal int CompareRData(const AuthRecord *const our, const CacheRecord *const pkt) +{ + mDNSu8 ourdata[256], *ourptr = ourdata, *ourend; + mDNSu8 pktdata[256], *pktptr = pktdata, *pktend; + if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); } + if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); } + + ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec); + pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec); + while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; } + if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict + + if (ourptr >= ourend) return(-1); // Our data ran out first; We lost + if (pktptr >= pktend) return(+1); // Packet data ran out first; We won + if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost + if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won + + LogMsg("CompareRData ERROR: Invalid state"); + return(-1); +} // See if we have an authoritative record that's identical to this packet record, // whose canonical DependentOn record is the specified master record. @@ -4783,46 +6864,46 @@ mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt) // 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); - } +{ + 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); - } +{ + 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. @@ -4834,1424 +6915,5464 @@ mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *co // 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 +{ + // If not supposed to be unique, not a conflict + if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse); + + // If a dependent record, not a conflict + if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse); + else + { + // If the pktrr matches a member of ourset, not a conflict + const AuthRecord *ourset = our->RRSet ? our->RRSet : our; + const AuthRecord *pktset = FindRRSet(m, pktrr); + if (pktset == ourset) return(mDNSfalse); + + // For records we're proxying, where we don't know the full + // relationship between the records, having any matching record + // in our AuthRecords list is sufficient evidence of non-conflict + if (our->WakeUp.HMAC.l[0] && pktset) return(mDNSfalse); + } + + // Okay, this is a conflict + return(mDNStrue); +} + +// Note: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, - DNSQuestion *q, AuthRecord *our) - { - int i; - const mDNSu8 *ptr = LocateAuthorities(query, end); - mDNSBool FoundUpdate = mDNSfalse; - - for (i = 0; i < query->h.numAuthorities; i++) - { - ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); - if (!ptr) break; - if (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)); + DNSQuestion *q, AuthRecord *our) +{ + int i; + const mDNSu8 *ptr = LocateAuthorities(query, end); + mDNSBool FoundUpdate = mDNSfalse; + + for (i = 0; i < query->h.numAuthorities; i++) + { + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (!ptr) break; + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + { + FoundUpdate = mDNStrue; + if (PacketRRConflict(m, our, &m->rec.r)) + { + int result = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass; + if (!result) result = (int)our->resrec.rrtype - (int)m->rec.r.resrec.rrtype; + if (!result) result = CompareRData(our, &m->rec.r); + if (result) + { + const char *const msg = (result < 0) ? "lost:" : (result > 0) ? "won: " : "tie: "; + LogMsg("ResolveSimultaneousProbe: %p Pkt Record: %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogMsg("ResolveSimultaneousProbe: %p Our Record %d %s %08lX %s", our->resrec.InterfaceID, our->ProbeCount, msg, our->resrec.rdatahash, ARDisplayString(m, our)); + } + // If we lost the tie-break for simultaneous probes, we don't immediately give up, because we might be seeing stale packets on the network. + // Instead we pause for one second, to give the other host (if real) a chance to establish its name, and then try probing again. + // If there really is another live host out there with the same name, it will answer our probes and we'll then rename. + if (result < 0) + { + m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond); + our->ProbeCount = DefaultProbeCountForTypeUnique; + our->AnnounceCount = InitialAnnounceCount; + InitializeLastAPTime(m, our); + goto exit; + } + } +#if 0 + else + { + LogMsg("ResolveSimultaneousProbe: %p Pkt Record: %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogMsg("ResolveSimultaneousProbe: %p Our Record %d ign: %08lX %s", our->resrec.InterfaceID, our->ProbeCount, our->resrec.rdatahash, ARDisplayString(m, our)); + } +#endif + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + if (!FoundUpdate) + LogInfo("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); exit: - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - } - -mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, 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); - } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it +} + +mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const ResourceRecord *const pktrr) +{ + mDNSu32 slot = HashSlot(pktrr->name); + CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr); + CacheRecord *rr; + mDNSBool match; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + if (!pktrr->InterfaceID) + { + mDNSu16 id1 = (pktrr->rDNSServer ? pktrr->rDNSServer->resGroupID : 0); + mDNSu16 id2 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); + match = (id1 == id2); + } + else match = (pktrr->InterfaceID == rr->resrec.InterfaceID); + + if (match && IdenticalSameNameRecord(pktrr, &rr->resrec)) break; + } + return(rr); +} +mDNSlocal void DeregisterProxyRecord(mDNS *const m, AuthRecord *const rr) +{ + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); +} + +mDNSlocal void ClearKeepaliveProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist, const mDNSInterfaceID InterfaceID) +{ + if (m->CurrentRecord) + LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = thelist; + + // Normally, the RDATA of the keepalive record will be different each time and hence we always + // clean up the keepalive record. + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) + { + if (mDNS_KeepaliveRecord(&m->rec.r.resrec)) + { + LogSPS("ClearKeepaliveProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s", + m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + DeregisterProxyRecord(m, rr); + } + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} + +// Called from mDNSCoreReceiveUpdate when we get a sleep proxy registration request, +// to check our lists and discard any stale duplicates of this record we already have +mDNSlocal void ClearIdenticalProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist) +{ + if (m->CurrentRecord) + LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = thelist; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) + if (IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec)) + { + LogSPS("ClearIdenticalProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s", + m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + DeregisterProxyRecord(m, rr); + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} + +// Called from ProcessQuery when we get an mDNS packet with an owner record in it +mDNSlocal void ClearProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist) +{ + if (m->CurrentRecord) + LogMsg("ClearProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = thelist; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) + if (owner->seq != rr->WakeUp.seq || m->timenow - rr->TimeRcvd > mDNSPlatformOneSecond * 60) + { + if (rr->AddressProxy.type == mDNSAddrType_IPv6) + { + // We don't do this here because we know that the host is waking up at this point, so we don't send + // Unsolicited Neighbor Advertisements -- even Neighbor Advertisements agreeing with what the host should be + // saying itself -- because it can cause some IPv6 stacks to falsely conclude that there's an address conflict. + #if MDNS_USE_Unsolicited_Neighbor_Advertisements + LogSPS("NDP Announcement -- Releasing traffic for H-MAC %.6a I-MAC %.6a %s", + &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, &rr->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth); + #endif + } + LogSPS("ClearProxyRecords: Removing %3d AC %2d %02X H-MAC %.6a I-MAC %.6a %d %d %s", + m->ProxyRecords, rr->AnnounceCount, rr->resrec.RecordType, + &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) rr->resrec.RecordType = kDNSRecordTypeShared; + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it, since real host is now back and functional + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} // ProcessQuery examines a received query to see if we have any answers to give mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, - const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, - mDNSBool QueryWasLocalUnicast, DNSMessage *const response) - { - mDNSBool FromLocalSubnet = 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)); + const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, + mDNSBool QueryWasLocalUnicast, DNSMessage *const response) +{ + mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL); + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + +#if POOF_ENABLED + CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated + CacheRecord **eap = &ExpectedAnswers; +#endif // POOF_ENABLED + + DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet + DNSQuestion **dqp = &DupQuestions; + mDNSs32 delayresponse = 0; + mDNSBool SendLegacyResponse = mDNSfalse; + const mDNSu8 *ptr; + mDNSu8 *responseptr = mDNSNULL; + AuthRecord *rr; + int i; + CacheRecord *McastNSEC3Records = mDNSNULL; + + // *** + // *** 1. Look in Additional Section for an OPT record + // *** + ptr = LocateOptRR(query, end, DNSOpt_OwnerData_ID_Space); + if (ptr) + { + ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *opt; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently + // delete all our own AuthRecords (which are identified by having zero MAC tags on them). + for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++) + if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0]) + { + ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords); + ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords); + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + // + // Look in Authority Section for NSEC3 record + // + + mDNSParseNSEC3Records(m, query, end, InterfaceID, &McastNSEC3Records); + + // *** + // *** 2. Parse Question Section and mark potential answers + // *** + ptr = query->data; + for (i=0; i<query->h.numQuestions; i++) // For each question... + { + mDNSBool QuestionNeedsMulticastResponse; + int NumAnswersForThisQuestion = 0; + AuthRecord *NSECAnswer = mDNSNULL; + DNSQuestion pktq, *q; + ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... + if (!ptr) goto exit; + + pktq.AnonInfo = mDNSNULL; + if (McastNSEC3Records) + InitializeAnonInfoForQuestion(m, &McastNSEC3Records, &pktq); + // 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); + + if (pktq.qclass & kDNSQClass_UnicastResponse) + m->mDNSStats.UnicastBitInQueries++; + else + m->mDNSStats.NormalQueries++; + + // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later + pktq.qclass &= ~kDNSQClass_UnicastResponse; + + // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe + // can result in user callbacks which may change the record list and/or question list. + // Also note: we just mark potential answer records here, without trying to build the + // "ResponseRecords" list, because we don't want to risk user callbacks deleting records + // from that list while we're in the middle of trying to build it. + if (m->CurrentRecord) + LogMsg("ProcessQuery ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (AnyTypeRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) + { + m->mDNSStats.MatchingAnswersForQueries++; + if (RRTypeAnswersQuestionType(&rr->resrec, pktq.qtype)) + { + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + ResolveSimultaneousProbe(m, query, end, &pktq, rr); + else if (ResourceRecordIsValidAnswer(rr)) + { + NumAnswersForThisQuestion++; + // As we have verified this question to be part of the same subset, + // set the anonymous data which is needed below when walk the cache + // records to see what answers we should be expecting. The cache records + // may cache only the nsec3RR and not the anonymous data itself. + if (pktq.AnonInfo && rr->resrec.AnonInfo) + SetAnonData(&pktq, &rr->resrec, mDNStrue); + + // Note: We should check here if this is a probe-type query, and if so, generate an immediate + // unicast answer back to the source, because timeliness in answering probes is important. + + // Notes: + // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast) + // NR_AnswerTo == NR_AnswerUnicast means "answer via delayed unicast" (to modern querier; may promote to multicast instead) + // NR_AnswerTo == NR_AnswerMulticast means "definitely answer via multicast" (can't downgrade to unicast later) + // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set, + // but the multicast querier is not on a matching subnet (e.g. because of overlaid subnets on one link) + // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source) + if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery)) + { + // We only mark this question for sending if it is at least one second since the last time we multicast it + // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it. + // This is to guard against the case where someone blasts us with queries as fast as they can. + if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 || + (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) + rr->NR_AnswerTo = NR_AnswerMulticast; + } + else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : NR_AnswerUnicast; + } + } + else if ((rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && ResourceRecordIsValidAnswer(rr)) + { + // If we don't have any answers for this question, but we do own another record with the same name, + // then we'll want to mark it to generate an NSEC record on this interface + if (!NSECAnswer) NSECAnswer = rr; + } + } + } + + if (NumAnswersForThisQuestion == 0 && NSECAnswer) + { + NumAnswersForThisQuestion++; + NSECAnswer->SendNSECNow = InterfaceID; + m->NextScheduledResponse = m->timenow; + } + + // If we couldn't answer this question, someone else might be able to, + // so use random delay on response to reduce collisions + if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms + + if (query->h.flags.b[0] & kDNSFlag0_TC) + m->mDNSStats.KnownAnswerMultiplePkts++; +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + if (QuestionNeedsMulticastResponse) +#else + // We only do the following accelerated cache expiration and duplicate question suppression processing + // for non-truncated multicast queries with multicast responses. + // For any query generating a unicast response we don't do this because we can't assume we will see the response. + // For truncated queries we don't do this because a response we're expecting might be suppressed by a subsequent + // known-answer packet, and when there's packet loss we can't safely assume we'll receive *all* known-answer packets. + if (QuestionNeedsMulticastResponse && !(query->h.flags.b[0] & kDNSFlag0_TC)) #endif - } - } - } - - // 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 POOF_ENABLED + const mDNSu32 slot = HashSlot(&pktq.qname); + CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname); + CacheRecord *cr; + + // Make a list indicating which of our own cache records we expect to see updated as a result of this query + // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) +#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit) + if (!cr->NextInKAList && eap != &cr->NextInKAList) + { + *eap = cr; + eap = &cr->NextInKAList; +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + if (cr->MPUnansweredQ == 0 || m->timenow - cr->MPLastUnansweredQT >= mDNSPlatformOneSecond) + { + // Although MPUnansweredQ is only really used for multi-packet query processing, + // we increment it for both single-packet and multi-packet queries, so that it stays in sync + // with the MPUnansweredKA value, which by necessity is incremented for both query types. + cr->MPUnansweredQ++; + cr->MPLastUnansweredQT = m->timenow; + cr->MPExpectingKA = mDNStrue; + } +#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + } +#endif // POOF_ENABLED + + // Check if this question is the same as any of mine. + // We only do this for non-truncated queries. Right now it would be too complicated to try + // to keep track of duplicate suppression state between multiple packets, especially when we + // can't guarantee to receive all of the Known Answer packets that go with a particular query. +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) +#endif + // For anonymous question, the duplicate suppressesion should happen if the + // question belongs in the same group. As the group is expected to be + // small, we don't do the optimization for now. + if (!pktq.AnonInfo) + { + 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; } + } + } + if (pktq.AnonInfo) + { + FreeAnonInfo(pktq.AnonInfo); + } + } + + // *** + // *** 3. Now we can safely build the list of marked answers + // *** + for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers + if (rr->NR_AnswerTo) // If we marked the record... + AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list + + // *** + // *** 4. Add additional records + // *** + AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); + + // *** + // *** 5. Parse Answer Section and cancel any records disallowed by Known-Answer list + // *** + for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section... + { + // Get the record... + CacheRecord *ourcacherr; + ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); + if (!ptr) goto exit; + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) + { + // See if this Known-Answer suppresses any of our currently planned answers + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + { + if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) + { + m->mDNSStats.KnownAnswerSuppressions++; + 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)) + { + m->mDNSStats.KnownAnswerSuppressions++; + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; + #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); + #endif + } + } + } + + ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); + + #if ENABLE_MULTI_PACKET_QUERY_SNOOPING + // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, + // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list). + if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) + { + ourcacherr->MPUnansweredKA++; + ourcacherr->MPExpectingKA = mDNSfalse; + } + #endif + +#if POOF_ENABLED + // Having built our ExpectedAnswers list from the questions in this packet, we then remove + // any records that are suppressed by the Known Answer list in this packet. + eap = &ExpectedAnswers; + while (*eap) + { + CacheRecord *cr = *eap; + if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec)) + { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; } + else eap = &cr->NextInKAList; + } +#endif // POOF_ENABLED + + // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. + if (!ourcacherr) + { + dqp = &DupQuestions; + while (*dqp) + { + DNSQuestion *q = *dqp; + if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } + else dqp = &q->NextInDQList; + } + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + // *** + // *** 6. Cancel any additionals that were added because of now-deleted records + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo)) + { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } + + // *** + // *** 7. Mark the send flags on the records we plan to send + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + { + if (rr->NR_AnswerTo) + { + mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response + mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response) + +#if !TARGET_OS_EMBEDDED + // always honor kDNSQClass_UnicastResponse in embedded environment to increase reliability + // in high multicast packet loss environments. + + // If it's been one TTL/4 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 == NR_AnswerUnicast) + { + m->mDNSStats.UnicastDemotedToMulticast++; + rr->NR_AnswerTo = NR_AnswerMulticast; + } + } +#endif // !TARGET_OS_EMBEDDED + + // If the client insists on a multicast response, then we'd better send one + if (rr->NR_AnswerTo == NR_AnswerMulticast) + { + m->mDNSStats.MulticastResponses++; + SendMulticastResponse = mDNStrue; + } + else if (rr->NR_AnswerTo == NR_AnswerUnicast) + { + m->mDNSStats.UnicastResponses++; + SendUnicastResponse = mDNStrue; + } + else if (rr->NR_AnswerTo) + { + SendLegacyResponse = mDNStrue; + } + + + if (SendMulticastResponse || SendUnicastResponse) + { #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - rr->ImmedAnswerMarkTime = m->timenow; + 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)) - { + 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 == NR_AnswerMulticast) + { + // Since additional records are an optimization anyway, we only ever send them on one interface at a time + // If two clients on different interfaces do queries that invoke the same optional additional answer, + // then the earlier client is out of luck + rr->ImmedAdditional = InterfaceID; + // No need to set m->NextScheduledResponse here + // We'll send these additional records when we send them, or not, as the case may be + } + } + + // *** + // *** 8. If we think other machines are likely to answer these questions, set our packet suppression timer + // *** + if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50)) + { #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - mDNSs32 oldss = m->SuppressSending; - if (oldss && delayresponse) - LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50); + 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; + // 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); + 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); + // *** + // *** 9. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too + // *** + if (SendLegacyResponse) + responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); exit: - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it - - // *** - // *** 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); - } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + + // *** + // *** 10. Finally, clear our link chains ready for use next time + // *** + while (ResponseRecords) + { + rr = ResponseRecords; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + } + +#if POOF_ENABLED + while (ExpectedAnswers) + { + CacheRecord *cr = ExpectedAnswers; + ExpectedAnswers = cr->NextInKAList; + cr->NextInKAList = mDNSNULL; + + // For non-truncated queries, we can definitively say that we should expect + // to be seeing a response for any records still left in the ExpectedAnswers list + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) + if (cr->UnansweredQueries == 0 || m->timenow - cr->LastUnansweredTime >= mDNSPlatformOneSecond) + { + cr->UnansweredQueries++; + cr->LastUnansweredTime = m->timenow; +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + if (cr->UnansweredQueries > 1) + debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", + cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); +#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + SetNextCacheCheckTimeForRecord(m, cr); + } + + // If we've seen multiple unanswered queries for this record, + // then mark it to expire in five seconds if we don't get a response by then. + if (cr->UnansweredQueries >= MaxUnansweredQueries) + { +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + // Only show debugging message if this record was not about to expire anyway + if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond) + debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); +#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + m->mDNSStats.PoofCacheDeletions++; + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + } +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + // Make a guess, based on the multi-packet query / known answer counts, whether we think we + // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for + // possible packet loss of up to 20% of the additional KA packets.) + else if (cr->MPUnansweredQ * 4 > cr->MPUnansweredKA * 5 + 8) + { + // We want to do this conservatively. + // If there are so many machines on the network that they have to use multi-packet known-answer lists, + // then we don't want them to all hit the network simultaneously with their final expiration queries. + // By setting the record to expire in four minutes, we achieve two things: + // (a) the 90-95% final expiration queries will be less bunched together + // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own + mDNSu32 remain = (mDNSu32)(RRExpireTime(cr) - m->timenow) / 4; + if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond) + remain = 240 * (mDNSu32)mDNSPlatformOneSecond; + + // Only show debugging message if this record was not about to expire anyway + if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond) + debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); + + if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) + cr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query + cr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics + cr->MPUnansweredKA = 0; + cr->MPExpectingKA = mDNSfalse; + + if (remain < kDefaultReconfirmTimeForNoAnswer) + remain = kDefaultReconfirmTimeForNoAnswer; + mDNS_Reconfirm_internal(m, cr, remain); + } +#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + } +#endif // POOF_ENABLED + + while (DupQuestions) + { + DNSQuestion *q = DupQuestions; + DupQuestions = q->NextInDQList; + q->NextInDQList = mDNSNULL; + i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); + debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, + srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); + } + + if (McastNSEC3Records) + { + debugf("ProcessQuery: McastNSEC3Records not used"); + FreeNSECRecords(m, McastNSEC3Records); + } + + 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 + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + mDNSu8 *responseend = mDNSNULL; + mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr && + !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL); + + if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr)) + { + LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes (Multicast, but no InterfaceID)", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); + return; + } + + verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); + + responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, + !mDNSSameIPPort(srcport, MulticastDNSPort), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg); + + if (responseend) // If responseend is non-null, that means we built a unicast response packet + { + debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld", + m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", + m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", + srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type); + mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, mDNSNULL, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + } +} + +#if 0 +mDNSlocal mDNSBool TrustedSource(const mDNS *const m, const mDNSAddr *const srcaddr) +{ + DNSServer *s; + (void)m; // Unused + (void)srcaddr; // Unused + for (s = m->DNSServers; s; s = s->next) + if (mDNSSameAddress(srcaddr, &s->addr)) return(mDNStrue); + return(mDNSfalse); +} +#endif + +struct UDPSocket_struct +{ + mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port +}; + +mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, const mDNSIPPort port, const mDNSOpaque16 id, const DNSQuestion *const question, mDNSBool tcp) +{ + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + { + if (!tcp && !q->LocalSocket) continue; + if (mDNSSameIPPort(tcp ? q->tcpSrcPort : q->LocalSocket->port, port) && + mDNSSameOpaque16(q->TargetQID, id) && + q->qtype == question->qtype && + q->qclass == question->qclass && + q->qnamehash == question->qnamehash && + SameDomainName(&q->qname, &question->qname)) + return(q); + } + return(mDNSNULL); +} + +// This function is called when we receive a unicast response. This could be the case of a unicast response from the +// DNS server or a response to the QU query. Hence, the cache record's InterfaceId can be both NULL or non-NULL (QU case) +mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, + const mDNSAddr *const srcaddr, const mDNSBool SrcLocal, const mDNSIPPort port, const mDNSOpaque16 id, const CacheRecord *const rr, mDNSBool tcp) +{ + DNSQuestion *q; + (void)id; + (void)srcaddr; + + for (q = m->Questions; q; q=q->next) + { + if (!q->DuplicateOf && ResourceRecordAnswersUnicastResponse(&rr->resrec, q)) + { + if (!mDNSOpaque16IsZero(q->TargetQID)) + { + debugf("ExpectingUnicastResponseForRecord msg->h.id %d q->TargetQID %d for %s", mDNSVal16(id), mDNSVal16(q->TargetQID), CRDisplayString(m, rr)); + + if (mDNSSameOpaque16(q->TargetQID, id)) + { + mDNSIPPort srcp; + if (!tcp) + { + if (q->LocalSocket) + srcp = q->LocalSocket->port; + else + srcp = zeroIPPort; + } + else + { + srcp = q->tcpSrcPort; + } + if (mDNSSameIPPort(srcp, port)) return(q); + + // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); + // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking + // if (TrustedSource(m, srcaddr)) return(mDNStrue); + LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) [q->Target %#a:%d] from %#a:%d %s", + q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(srcp), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); + return(mDNSNULL); + } + } + else + { + if (SrcLocal && q->ExpectUnicastResp && (mDNSu32)(m->timenow - q->ExpectUnicastResp) < (mDNSu32)(mDNSPlatformOneSecond*2)) + return(q); + } + } + } + return(mDNSNULL); +} + +// Return a pointer to the primary service name, skipping subtype name if present. +mDNSlocal const domainname *getPrimaryServiceName(const domainname *domainName) +{ + const domainname *primaryName = domainName; + const domainname *subName = SkipLeadingLabels(domainName, 1); + + if (SameDomainLabel(subName->c, (const mDNSu8 *)mDNSSubTypeLabel)) + { + // skip "<sub type name>._sub" portion of name + primaryName = SkipLeadingLabels(domainName, 2); + debugf("getPrimaryServiceName: returning %##s for _sub type", primaryName); + } + + return primaryName; +} + +// This function is not called if the packet is from us, which implies that we accept all multicast packets coming from us. +mDNSlocal mDNSBool ExpectingMulticastResponseForRecord(mDNS *const m, CacheRecord *rr, const mDNSAddr *srcaddr, mDNSBool recordAccepted, + CacheRecord **McastNSEC3Records) +{ + DNSQuestion *q; + + // Accept A and AAAA if we accepted something before in the same packet as most likely related to the + // service records that we may have accepted. + if (recordAccepted && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA)) + { + LogInfo("ExpectingMulticastResponseForRecord:A:AAAA: accepting %s, from %#a due to same packet %d", CRDisplayString(m, rr), srcaddr, m->PktNum); + return mDNStrue; + } + for (q = m->Questions; q; q=q->next) + { + if (!q->DuplicateOf && mDNSOpaque16IsZero(q->TargetQID)) + { + mDNSBool ret; + // 1. If a resource record answers question, cache it. This also will cache NSECs if it asserts + // non-existence of q->qtype. If we have any matching NSEC3 Records for the question, send + // it along with the resource record. Do it only for questions that are expecting to + // discover only its peers (q->AnonInfo not NULL) + if (q->AnonInfo && McastNSEC3Records && !rr->resrec.AnonInfo) + { + InitializeAnonInfoForCR(m, McastNSEC3Records, rr); + } + ret = ResourceRecordAnswersQuestion(&rr->resrec, q); + if (ret) + { + // The record and the question belong to the same subset. Set the + // anonymous data in the cache record. + if (q->AnonInfo && rr->resrec.AnonInfo) + { + SetAnonData(q, &rr->resrec, mDNSfalse); + } + LogInfo("ExpectingMulticastResponseForRecord: Name and Type match, accepting %s, from %#a", CRDisplayString(m, rr), srcaddr); + if (rr->resrec.rrtype == kDNSType_NSEC) + LogInfo("ExpectingMulticastResponseForRecord: record %s, question %##s (%s)", CRDisplayString(m, rr), q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; + } + if (rr->resrec.rrtype == kDNSType_SRV || rr->resrec.rrtype == kDNSType_TXT) + { + // Point to the service type in the record name + const domainname *name = SkipLeadingLabels(rr->resrec.name, 1); + + // If question is for a sub type, just compare against the primary service type + const domainname *primaryName = getPrimaryServiceName(&q->qname); + + // 2. If the SRV or TXT record matches the service name, then cache it. If the TXT or SRV record is + // before the PTR record in the packet, PTR record may not be in the cache yet and hence the logic + // in (3) below will fail to cache it. + if (q->qtype == kDNSType_PTR && name && SameDomainName(primaryName, name)) + { + LogInfo("ExpectingMulticastResponseForRecord: Accepting %s due to PTR match, question %##s from %#a, pktnum %d", + CRDisplayString(m, rr), q->qname.c, srcaddr, m->PktNum); + return mDNStrue; + } + + if (name) + { + const mDNSu32 slot = HashSlot(name); + const mDNSu32 namehash = DomainNameHashValue(name); + CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); + CacheRecord *cr; + + // 3. Same as in (2), but look in the cache in case we don't have the PTR question. + + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + { + if (cr->resrec.rrtype == kDNSType_PTR) + { + primaryName = getPrimaryServiceName(cr->resrec.name); + + if (SameDomainName(primaryName, name)) + { + LogInfo("ExpectingMulticastResponseForRecord: accepting %s, from %#a, pktnum %d", + CRDisplayString(m, rr), srcaddr, m->PktNum); + return mDNStrue; + } + } + } + } + } + } + } + debugf("ExpectingMulticastResponseForRecord: discarding %s, from %#a, pktnum %d", CRDisplayString(m, rr), srcaddr, m->PktNum); + return(mDNSfalse); +} + +// Certain data types need more space for in-memory storage than their in-packet rdlength would imply +// Currently this applies only to rdata types containing more than one domainname, +// or types where the domainname is not the last item in the structure. +mDNSlocal mDNSu16 GetRDLengthMem(const ResourceRecord *const rr) +{ + switch (rr->rrtype) + { + case kDNSType_SOA: return sizeof(rdataSOA); + case kDNSType_RP: return sizeof(rdataRP); + case kDNSType_PX: return sizeof(rdataPX); + default: return rr->rdlength; + } +} + +mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay, mDNSBool Add, const mDNSAddr *sourceAddress) +{ + CacheRecord *rr = mDNSNULL; + mDNSu16 RDLength = GetRDLengthMem(&m->rec.r.resrec); + + if (!m->rec.r.resrec.InterfaceID) debugf("CreateNewCacheEntry %s", CRDisplayString(m, &m->rec.r)); + + //if (RDLength > InlineCacheRDSize) + // LogInfo("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r)); + + if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec); // If we don't have a CacheGroup for this name, make one now + if (cg) rr = GetCacheRecord(m, cg, RDLength); // Make a cache record, being careful not to recycle cg + if (!rr) NoCacheAnswer(m, &m->rec.r); + else + { + RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer + *rr = m->rec.r; // Block copy the CacheRecord object + rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment + rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header + + // We need to add the anonymous info before we call CacheRecordAdd so that + // if it finds a matching question with this record, it bumps up the counters like + // CurrentAnswers etc. Otherwise, when a cache entry gets removed, CacheRecordRmv + // will complain. + if (m->rec.r.resrec.AnonInfo) + { + rr->resrec.AnonInfo = m->rec.r.resrec.AnonInfo; + m->rec.r.resrec.AnonInfo = mDNSNULL; + } + rr->DelayDelivery = delay; + + // If this is an oversized record with external storage allocated, copy rdata to external storage + if (rr->resrec.rdata == (RData*)&rr->smallrdatastorage && RDLength > InlineCacheRDSize) + LogMsg("rr->resrec.rdata == &rr->rdatastorage but length > InlineCacheRDSize %##s", m->rec.r.resrec.name->c); + else if (rr->resrec.rdata != (RData*)&rr->smallrdatastorage && RDLength <= InlineCacheRDSize) + LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c); + if (RDLength > InlineCacheRDSize) + mDNSPlatformMemCopy(rr->resrec.rdata, m->rec.r.resrec.rdata, sizeofRDataHeader + RDLength); + + rr->next = mDNSNULL; // Clear 'next' pointer + rr->nsec = mDNSNULL; + rr->soa = mDNSNULL; + + if (sourceAddress) + rr->sourceAddress = *sourceAddress; + + if (!rr->resrec.InterfaceID) + { + m->rrcache_totalused_unicast += rr->resrec.rdlength; + if (DNSSECRecordType(rr->resrec.rrtype)) + BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeMemoryUsage, rr->resrec.rdlength); + } + + if (Add) + { + *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list + cg->rrcache_tail = &(rr->next); // Advance tail pointer + CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTimeForRecord(m, rr); for us + } + else + { + // Can't use the "cg->name" if we are not adding to the cache as the + // CacheGroup may be released anytime if it is empty + domainname *name = mDNSPlatformMemAllocate(DomainNameLength(cg->name)); + if (name) + { + AssignDomainName(name, cg->name); + rr->resrec.name = name; + } + else + { + ReleaseCacheRecord(m, rr); + NoCacheAnswer(m, &m->rec.r); + rr = mDNSNULL; + } + } + } + return(rr); +} + +mDNSlocal void RefreshCacheRecord(mDNS *const m, CacheRecord *rr, mDNSu32 ttl) +{ + rr->TimeRcvd = m->timenow; + rr->resrec.rroriginalttl = ttl; + rr->UnansweredQueries = 0; +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + rr->MPUnansweredQ = 0; + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; +#endif + SetNextCacheCheckTimeForRecord(m, rr); +} + +mDNSexport void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease) +{ + CacheRecord *rr; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (rr->CRActiveQuestion == q) + { + //LogInfo("GrantCacheExtensions: new lease %d / %s", lease, CRDisplayString(m, rr)); + RefreshCacheRecord(m, rr, lease); + } +} + +mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // TTL in seconds +{ + if (LLQType == uDNS_LLQ_Entire) ttl = kLLQ_DefLease; + else if (LLQType == uDNS_LLQ_Events) + { + // If the TTL is -1 for uDNS LLQ event packet, that means "remove" + if (ttl == 0xFFFFFFFF) ttl = 0; + else ttl = kLLQ_DefLease; + } + else // else not LLQ (standard uDNS response) + { + // The TTL is already capped to a maximum value in GetLargeResourceRecord, but just to be extra safe we + // also do this check here to make sure we can't get overflow below when we add a quarter to the TTL + if (ttl > 0x60000000UL / mDNSPlatformOneSecond) ttl = 0x60000000UL / mDNSPlatformOneSecond; + + ttl = RRAdjustTTL(ttl); + + // For mDNS, TTL zero means "delete this record" + // For uDNS, TTL zero means: this data is true at this moment, but don't cache it. + // For the sake of network efficiency, we impose a minimum effective TTL of 15 seconds. + // This means that we'll do our 80, 85, 90, 95% queries at 12.00, 12.75, 13.50, 14.25 seconds + // respectively, and then if we get no response, delete the record from the cache at 15 seconds. + // This gives the server up to three seconds to respond between when we send our 80% query at 12 seconds + // and when we delete the record at 15 seconds. Allowing cache lifetimes less than 15 seconds would + // (with the current code) result in the server having even less than three seconds to respond + // before we deleted the record and reported a "remove" event to any active questions. + // Furthermore, with the current code, if we were to allow a TTL of less than 2 seconds + // then things really break (e.g. we end up making a negative cache entry). + // In the future we may want to revisit this and consider properly supporting non-cached (TTL=0) uDNS answers. + if (ttl < 15) ttl = 15; + } + + return ttl; +} + +// When the response does not match the question directly, we still want to cache them sometimes. The current response is +// in m->rec. +mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist, DNSQuestion *q, mDNSBool *nseclist) +{ + CacheRecord *const newcr = &m->rec.r; + ResourceRecord *rr = &newcr->resrec; + const CacheRecord *cr; + + *nseclist = mDNSfalse; + for (cr = crlist; cr != (CacheRecord*)1; cr = cr->NextInCFList) + { + domainname *target = GetRRDomainNameTarget(&cr->resrec); + // When we issue a query for A record, the response might contain both a CNAME and A records. Only the CNAME would + // match the question and we already created a cache entry in the previous pass of this loop. Now when we process + // the A record, it does not match the question because the record name here is the CNAME. Hence we try to + // match with the previous records to make it an AcceptableResponse. We have to be careful about setting the + // DNSServer value that we got in the previous pass. This can happen for other record types like SRV also. + + if (target && cr->resrec.rdatahash == rr->namehash && SameDomainName(target, rr->name)) + { + LogInfo("IsResponseAcceptable: Found a matching entry for %##s in the CacheFlushRecords %s", rr->name->c, CRDisplayString(m, cr)); + return (mDNStrue); + } + } + + // Either the question requires validation or we are validating a response with DNSSEC in which case + // we need to accept the RRSIGs also so that we can validate the response. It is also possible that + // we receive NSECs for our query which does not match the qname and we need to cache in that case + // too. nseclist is set if they have to be cached as part of the negative cache record. + if (q && DNSSECQuestion(q)) + { + mDNSBool same = SameDomainName(&q->qname, rr->name); + if (same && (q->qtype == rr->rrtype || rr->rrtype == kDNSType_CNAME)) + { + LogInfo("IsResponseAcceptable: Accepting, same name and qtype %s, CR %s", DNSTypeName(q->qtype), + CRDisplayString(m, newcr)); + return mDNStrue; + } + // We cache RRSIGS if it covers the question type or NSEC. If it covers a NSEC, + // "nseclist" is set + if (rr->rrtype == kDNSType_RRSIG) + { + RDataBody2 *const rdb = (RDataBody2 *)newcr->smallrdatastorage.data; + rdataRRSig *rrsig = &rdb->rrsig; + mDNSu16 typeCovered = swap16(rrsig->typeCovered); + + // Note the ordering. If we are looking up the NSEC record, then the RRSIG's typeCovered + // would match the qtype and they are cached normally as they are not used to prove the + // non-existence of any name. In that case, it is like any other normal dnssec validation + // and hence nseclist should not be set. + + if (same && ((typeCovered == q->qtype) || (typeCovered == kDNSType_CNAME))) + { + LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches question type %s", CRDisplayString(m, newcr), + DNSTypeName(q->qtype)); + return mDNStrue; + } + else if (typeCovered == kDNSType_NSEC || typeCovered == kDNSType_NSEC3) + { + LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches %s type (nseclist = 1)", CRDisplayString(m, newcr), DNSTypeName(typeCovered)); + *nseclist = mDNStrue; + return mDNStrue; + } + else if (typeCovered == kDNSType_SOA) + { + LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches SOA type (nseclist = 1)", CRDisplayString(m, newcr)); + *nseclist = mDNStrue; + return mDNStrue; + } + else return mDNSfalse; + } + if (rr->rrtype == kDNSType_NSEC) + { + if (!UNICAST_NSEC(rr)) + { + LogMsg("IsResponseAcceptable: ERROR!! Not a unicast NSEC %s", CRDisplayString(m, newcr)); + return mDNSfalse; + } + LogInfo("IsResponseAcceptable: Accepting NSEC %s (nseclist = 1)", CRDisplayString(m, newcr)); + *nseclist = mDNStrue; + return mDNStrue; + } + if (rr->rrtype == kDNSType_SOA) + { + LogInfo("IsResponseAcceptable: Accepting SOA %s (nseclist = 1)", CRDisplayString(m, newcr)); + *nseclist = mDNStrue; + return mDNStrue; + } + else if (rr->rrtype == kDNSType_NSEC3) + { + LogInfo("IsResponseAcceptable: Accepting NSEC3 %s (nseclist = 1)", CRDisplayString(m, newcr)); + *nseclist = mDNStrue; + return mDNStrue; + } + } + return mDNSfalse; +} + +mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords) +{ + CacheRecord *rp, *next; + + for (rp = NSECRecords; rp; rp = next) + { + next = rp->next; + ReleaseCacheRecord(m, rp); + } +} + +// If we received zero DNSSEC records even when the DO/EDNS0 bit was set, we need to provide this +// information to ValidatingResponse question to indicate the DNSSEC status to the application +mDNSlocal void mDNSCoreReceiveNoDNSSECAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr, + mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) +{ + int i; + const mDNSu8 *ptr = response->data; + + for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) + { + DNSQuestion pktq; + DNSQuestion *qptr = mDNSNULL; + ptr = getQuestion(response, ptr, end, InterfaceID, &pktq); + if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &pktq, !dstaddr)) && + qptr->ValidatingResponse) + { + DNSQuestion *next, *q; + + if (qptr->DuplicateOf) + LogMsg("mDNSCoreReceiveNoDNSSECAnswers: ERROR!! qptr %##s (%s) Duplicate question matching response", qptr->qname.c, DNSTypeName(qptr->qtype)); + + // Be careful to call the callback for duplicate questions first and then the original + // question. If we called the callback on the original question, it could stop and + // a duplicate question would become the original question. + mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls + for (q = qptr->next ; q && q != m->NewQuestions; q = next) + { + next = q->next; + if (q->DuplicateOf == qptr) + { + if (q->ValidatingResponse) + LogInfo("mDNSCoreReceiveNoDNSSECAnswers: qptr %##s (%s) Duplicate question found", q->qname.c, DNSTypeName(q->qtype)); + else + LogMsg("mDNSCoreReceiveNoDNSSECAnswers: ERROR!! qptr %##s (%s) Duplicate question not ValidatingResponse", q->qname.c, DNSTypeName(q->qtype)); + if (q->QuestionCallback) + q->QuestionCallback(m, q, mDNSNULL, QC_nodnssec); + } + } + if (qptr->QuestionCallback) + qptr->QuestionCallback(m, qptr, mDNSNULL, QC_nodnssec); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + } +} + +mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr, + mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, uDNS_LLQType LLQType, mDNSu8 rcode, CacheRecord *NSECRecords) +{ + int i; + const mDNSu8 *ptr = response->data; + CacheRecord *SOARecord = mDNSNULL; + + for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) + { + DNSQuestion q; + DNSQuestion *qptr = mDNSNULL; + ptr = getQuestion(response, ptr, end, InterfaceID, &q); + if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr))) + { + CacheRecord *rr, *neg = mDNSNULL; + mDNSu32 slot = HashSlot(&q.qname); + CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) + { + // 1. If we got a fresh answer to this query, then don't need to generate a negative entry + if (RRExpireTime(rr) - m->timenow > 0) break; + // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr; + } + // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft + // Active Directory sites) we don't want to waste memory making negative cache entries for all the unicast answers. + // Otherwise we just fill up our cache with negative entries for just about every single multicast name we ever look up + // (since the Microsoft Active Directory server is going to assert that pretty much every single multicast name doesn't exist). + // This is not only a waste of memory, but there's also the problem of those negative entries confusing us later -- e.g. we + // suppress sending our mDNS query packet because we think we already have a valid (negative) answer to that query in our cache. + // The one exception is that we *DO* want to make a negative cache entry for "local. SOA", for the (common) case where we're + // *not* on a Microsoft Active Directory network, and there is no authoritative server for "local". Note that this is not + // in conflict with the mDNS spec, because that spec says, "Multicast DNS Zones have no SOA record," so it's okay to cache + // negative answers for "local. SOA" from a uDNS server, because the mDNS spec already says that such records do not exist :-) + // + // By suppressing negative responses, it might take longer to timeout a .local question as it might be expecting a + // response e.g., we deliver a positive "A" response and suppress negative "AAAA" response and the upper layer may + // be waiting longer to get the AAAA response before returning the "A" response to the application. To handle this + // case without creating the negative cache entries, we generate a negative response and let the layer above us + // do the appropriate thing. This negative response is also needed for appending new search domains. + if (!InterfaceID && q.qtype != kDNSType_SOA && IsLocalDomain(&q.qname)) + { + if (!rr) + { + LogInfo("mDNSCoreReceiveNoUnicastAnswers: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + m->CurrentQuestion = qptr; + // We are not creating a cache record in this case, we need to pass back + // the error we got so that the proxy code can return the right one to + // the application + if (qptr->ProxyQuestion) + qptr->responseFlags = response->h.flags; + GenerateNegativeResponse(m, QC_forceresponse); + m->CurrentQuestion = mDNSNULL; + } + else + { + LogInfo("mDNSCoreReceiveNoUnicastAnswers: Skipping check and not creating a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + } + } + else + { + if (!rr) + { + // We start off assuming a negative caching TTL of 60 seconds + // but then look to see if we can find an SOA authority record to tell us a better value we should be using + mDNSu32 negttl = 60; + int repeat = 0; + const domainname *name = &q.qname; + mDNSu32 hash = q.qnamehash; + + // Special case for our special Microsoft Active Directory "local SOA" check. + // Some cheap home gateways don't include an SOA record in the authority section when + // they send negative responses, so we don't know how long to cache the negative result. + // Because we don't want to keep hitting the root name servers with our query to find + // if we're on a network using Microsoft Active Directory using "local" as a private + // internal top-level domain, we make sure to cache the negative result for at least one day. + if (q.qtype == kDNSType_SOA && SameDomainName(&q.qname, &localdomain)) negttl = 60 * 60 * 24; + + // If we're going to make (or update) a negative entry, then look for the appropriate TTL from the SOA record + if (response->h.numAuthorities && (ptr = LocateAuthorities(response, end)) != mDNSNULL) + { + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA) + { + const mDNSu32 s = HashSlot(m->rec.r.resrec.name); + CacheGroup *cgSOA = CacheGroupForRecord(m, s, &m->rec.r.resrec); + const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data; + mDNSu32 ttl_s = soa->min; + // We use the lesser of the SOA.MIN field and the SOA record's TTL, *except* + // for the SOA record for ".", where the record is reported as non-cacheable + // (TTL zero) for some reason, so in this case we just take the SOA record's TTL as-is + if (ttl_s > m->rec.r.resrec.rroriginalttl && m->rec.r.resrec.name->c[0]) + ttl_s = m->rec.r.resrec.rroriginalttl; + if (negttl < ttl_s) negttl = ttl_s; + + // Create the SOA record as we may have to return this to the questions + // that we are acting as a proxy for currently or in the future. + SOARecord = CreateNewCacheEntry(m, s, cgSOA, 1, mDNSfalse, mDNSNULL); + + // Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer, + // with an Authority Section SOA record for d.com, then this is a hint that the authority + // is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either. + // To do this we set the repeat count so the while loop below will make a series of negative cache entries for us + // + // For ProxyQuestions, we don't do this as we need to create additional SOA records to cache them + // along with the negative cache record. For simplicity, we don't create the additional records. + if (!qptr->ProxyQuestion && q.qtype == kDNSType_SOA) + { + int qcount = CountLabels(&q.qname); + int scount = CountLabels(m->rec.r.resrec.name); + if (qcount - 1 > scount) + if (SameDomainName(SkipLeadingLabels(&q.qname, qcount - scount), m->rec.r.resrec.name)) + repeat = qcount - 1 - scount; + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + // If we already had a negative entry in the cache, then we double our existing negative TTL. This is to avoid + // the case where the record doesn't exist (e.g. particularly for things like our lb._dns-sd._udp.<domain> query), + // and the server returns no SOA record (or an SOA record with a small MIN TTL) so we assume a TTL + // of 60 seconds, and we end up polling the server every minute for a record that doesn't exist. + // With this fix in place, when this happens, we double the effective TTL each time (up to one hour), + // so that we back off our polling rate and don't keep hitting the server continually. + if (neg) + { + if (negttl < neg->resrec.rroriginalttl * 2) + negttl = neg->resrec.rroriginalttl * 2; + if (negttl > 3600) + negttl = 3600; + } + + negttl = GetEffectiveTTL(LLQType, negttl); // Add 25% grace period if necessary + + // If we already had a negative cache entry just update it, else make one or more new negative cache entries. + if (neg) + { + LogInfo("mDNSCoreReceiveNoUnicastAnswers: Renewing negative TTL from %d to %d %s", neg->resrec.rroriginalttl, negttl, CRDisplayString(m, neg)); + RefreshCacheRecord(m, neg, negttl); + // When we created the cache for the first time and answered the question, the question's + // interval was set to MaxQuestionInterval. If the cache is about to expire and we are resending + // the queries, the interval should still be at MaxQuestionInterval. If the query is being + // restarted (setting it to InitialQuestionInterval) for other reasons e.g., wakeup, + // we should reset its question interval here to MaxQuestionInterval. + ResetQuestionState(m, qptr); + if (DNSSECQuestion(qptr)) + neg->CRDNSSECQuestion = 1; + // Update the NSEC records again. + // TBD: Need to purge and revalidate if the cached NSECS and the new set are not same. + if (NSECRecords) + { + if (!AddNSECSForCacheRecord(m, NSECRecords, neg, rcode)) + { + // We might just have an SOA record for zones that are not signed and hence don't log + // this as an error + LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s during refresh", CRDisplayString(m, neg)); + FreeNSECRecords(m, NSECRecords); + neg->CRDNSSECQuestion = 0; + } + NSECRecords = mDNSNULL; + } + if (SOARecord) + { + if (neg->soa) + ReleaseCacheRecord(m, neg->soa); + neg->soa = SOARecord; + SOARecord = mDNSNULL; + } + } + else while (1) + { + CacheRecord *negcr; + debugf("mDNSCoreReceiveNoUnicastAnswers making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype)); + MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer); + m->rec.r.responseFlags = response->h.flags; + // We create SOA records above which might create new cache groups. Earlier + // in the function we looked up the cache group for the name and it could have + // been NULL. If we pass NULL cg to new cache entries that we create below, + // it will create additional cache groups for the same name. To avoid that, + // look up the cache group again to re-initialize cg again. + cg = CacheGroupForName(m, slot, hash, name); + if (NSECRecords && DNSSECQuestion(qptr)) + { + // Create the cache entry with delay and then add the NSEC records + // to it and add it immediately. + negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL); + if (negcr) + { + negcr->CRDNSSECQuestion = 0; + if (!AddNSECSForCacheRecord(m, NSECRecords, negcr, rcode)) + { + LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s", + CRDisplayString(m, negcr)); + FreeNSECRecords(m, NSECRecords); + } + else + { + negcr->CRDNSSECQuestion = 1; + LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord added neg NSEC for %s", CRDisplayString(m, negcr)); + } + NSECRecords = mDNSNULL; + negcr->DelayDelivery = 0; + CacheRecordDeferredAdd(m, negcr); + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + break; + } + else + { + // Need to add with a delay so that we can tag the SOA record + negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL); + if (negcr) + { + negcr->CRDNSSECQuestion = 0; + if (DNSSECQuestion(qptr)) + negcr->CRDNSSECQuestion = 1; + negcr->DelayDelivery = 0; + + if (SOARecord) + { + if (negcr->soa) + ReleaseCacheRecord(m, negcr->soa); + negcr->soa = SOARecord; + SOARecord = mDNSNULL; + } + CacheRecordDeferredAdd(m, negcr); + } + } + m->rec.r.responseFlags = zeroID; + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + if (!repeat) break; + repeat--; + name = (const domainname *)(name->c + 1 + name->c[0]); + hash = DomainNameHashValue(name); + slot = HashSlot(name); + cg = CacheGroupForName(m, slot, hash, name); + } + } + } + } + } + if (NSECRecords) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: NSECRecords not used"); FreeNSECRecords(m, NSECRecords); } + if (SOARecord) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: SOARecord not used"); ReleaseCacheRecord(m, SOARecord); } +} + +mDNSlocal void mDNSCorePrintStoredProxyRecords(mDNS *const m) +{ + AuthRecord *rrPtr = mDNSNULL; + LogSPS("Stored Proxy records :"); + for (rrPtr = m->SPSRRSet; rrPtr; rrPtr = rrPtr->next) + { + LogSPS("%s", ARDisplayString(m, rrPtr)); + } +} + +mDNSlocal mDNSBool mDNSCoreRegisteredProxyRecord(mDNS *const m, AuthRecord *rr) +{ + AuthRecord *rrPtr = mDNSNULL; + + for (rrPtr = m->SPSRRSet; rrPtr; rrPtr = rrPtr->next) + { + if (IdenticalResourceRecord(&rrPtr->resrec, &rr->resrec)) + { + LogSPS("mDNSCoreRegisteredProxyRecord: Ignoring packet registered with sleep proxy : %s ", ARDisplayString(m, rr)); + return mDNStrue; + } + } + mDNSCorePrintStoredProxyRecords(m); + return mDNSfalse; +} + +mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage *const response, uDNS_LLQType LLQType, + const mDNSu32 slot, CacheGroup *cg, DNSQuestion *unicastQuestion, CacheRecord ***cfp, CacheRecord **NSECCachePtr, + mDNSInterfaceID InterfaceID) +{ + CacheRecord *rr; + CacheRecord **cflocal = *cfp; + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + mDNSBool match; + // Resource record received via unicast, the resGroupID should match ? + if (!InterfaceID) + { + mDNSu16 id1 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); + mDNSu16 id2 = (m->rec.r.resrec.rDNSServer ? m->rec.r.resrec.rDNSServer->resGroupID : 0); + match = (id1 == id2); + } + else + match = (rr->resrec.InterfaceID == InterfaceID); + // If we found this exact resource record, refresh its TTL + if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) + { + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) + verbosedebugf("mDNSCoreReceiveCacheCheck: Found record size %5d interface %p already in cache: %s", + m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); + + if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list + if (rr->NextInCFList == mDNSNULL && *cfp != &rr->NextInCFList && LLQType != uDNS_LLQ_Events) + { + *cflocal = rr; + cflocal = &rr->NextInCFList; + *cflocal = (CacheRecord*)1; + *cfp = &rr->NextInCFList; + } + + // If this packet record is marked unique, and our previous cached copy was not, then fix it + if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + { + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + q->UniqueAnswers++; + } + rr->resrec.RecordType = m->rec.r.resrec.RecordType; + } + } + + if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS)) + { + // If the rdata of the packet record differs in name capitalization from the record in our cache + // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get + // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. + // <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing + rr->resrec.rroriginalttl = 0; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change old: %s", CRDisplayString(m, rr)); + LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change new: %s", CRDisplayString(m, &m->rec.r)); + LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change in %d slot %3d in %d %d", + NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow); + // DO NOT break out here -- we want to continue as if we never found it + } + else if (!IdenticalAnonInfo(m->rec.r.resrec.AnonInfo, rr->resrec.AnonInfo)) + { + // If the NSEC3 record changed, a few possibilities + // + // 1) the peer reinitialized e.g., after network change and still part of the + // same set. + // 2) the peer went to a different set but we did not see the goodbyes. If we just + // update the nsec3 record, it would be incorrect. Flush the cache so that we + // can deliver a RMV followed by ADD. + // 3) if the peer is ourselves and we see the goodbye when moving to a different set + // and so we flush the cache and create a new cache record with the new set information. + // Now we move back to the original set. In this case, we can't just update the + // NSEC3 record alone. We need to flush so that we can deliver an RMV followed by ADD + // when we create the new cache entry. + // + // Note: For case (1), we could avoid flushing the cache but we can't tell the difference + // from the other cases. + rr->resrec.rroriginalttl = 0; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + LogInfo("mDNSCoreReceiveCacheCheck: AnonInfo changed for %s", CRDisplayString(m, rr)); + // DO NOT break out here -- we want to continue as if we never found it. When we return + // from this function, we will create a new cache entry with the new NSEC3 record + } + else if (m->rec.r.resrec.rroriginalttl > 0) + { + DNSQuestion *q; + + m->mDNSStats.CacheRefreshed++; + + if (rr->resrec.rroriginalttl == 0) debugf("uDNS rescuing %s", CRDisplayString(m, rr)); + RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); + rr->responseFlags = response->h.flags; + + // If we may have NSEC records returned with the answer (which we don't know yet as it + // has not been processed), we need to cache them along with the first cache + // record in the list that answers the question so that it can be used for validation + // later. The "type" check below is to make sure that we cache on the cache record + // that would answer the question. It is possible that we might cache additional things + // e.g., MX question might cache A records also, and we want to cache the NSEC on + // the record that answers the question. + if (response->h.numAnswers && unicastQuestion && unicastQuestion->qtype == rr->resrec.rrtype + && !(*NSECCachePtr)) + { + LogInfo("mDNSCoreReceiveCacheCheck: rescuing RR %s", CRDisplayString(m, rr)); + *NSECCachePtr = rr; + } + // We have to reset the question interval to MaxQuestionInterval so that we don't keep + // polling the network once we get a valid response back. For the first time when a new + // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. + // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server + // configuration changed, without flushing the cache, we reset the question interval here. + // Currently, we do this for for both multicast and unicast questions as long as the record + // type is unique. For unicast, resource record is always unique and for multicast it is + // true for records like A etc. but not for PTR. + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + for (q = m->Questions; q; q=q->next) + { + if (!q->DuplicateOf && !q->LongLived && + ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + ResetQuestionState(m, q); + debugf("mDNSCoreReceiveCacheCheck: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + break; // Why break here? Aren't there other questions we might want to look at?-- SC July 2010 + } + } + } + break; + } + else + { + + // If the packet TTL is zero, that means we're deleting this record. + // To give other hosts on the network a chance to protest, we push the deletion + // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. + // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent + // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. + // If record's current expiry time is more than a second from now, we set it to expire in one second. + // If the record is already going to expire in less than one second anyway, we leave it alone -- + // we don't want to let the goodbye packet *extend* the record's lifetime in our cache. + debugf("DE for %s", CRDisplayString(m, rr)); + if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond) + { + rr->resrec.rroriginalttl = 1; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + } + break; + } + } + } + return rr; +} + +mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, + const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records) +{ + const mDNSu8 *ptr = response->data; + CacheRecord *rr; + int i; + + if (!response->h.numAuthorities) + return; + ptr = LocateAuthorities(response, end); + if (!ptr) + { + LogInfo("mDNSParseNSEC3Records: ERROR can't locate authorities"); + return; + } + for (i = 0; i < response->h.numAuthorities && ptr && ptr < end; i++) + { + mDNSu32 slot; + CacheGroup *cg; + + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative || m->rec.r.resrec.rrtype != kDNSType_NSEC3) + { + debugf("mDNSParseNSEC3Records: ptr %p, Record %s, ignoring", ptr, CRDisplayString(m, &m->rec.r)); + m->rec.r.resrec.RecordType = 0; + continue; + } + slot = HashSlot(m->rec.r.resrec.name); + cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); + // Create the cache entry but don't add it to the cache it. We need + // to cache this along with the main cache record. + rr = CreateNewCacheEntry(m, slot, cg, 0, mDNSfalse, mDNSNULL); + if (rr) + { + debugf("mDNSParseNSEC3Records: %s", CRDisplayString(m, rr)); + *NSEC3Records = rr; + NSEC3Records = &rr->next; + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } +} + +mDNSlocal void mDNSCoreResetRecord(mDNS *const m) +{ + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + if (m->rec.r.resrec.AnonInfo) + { + FreeAnonInfo(m->rec.r.resrec.AnonInfo); + m->rec.r.resrec.AnonInfo = mDNSNULL; + } +} + +#define DEVICE_INFO_RECORD_LABELS 4 + +// Determine if the record is an instance of _device-info._tcp.local. +mDNSlocal mDNSBool IsDeviceInfoRecord(const domainname *d) +{ + const domainname *afterInstance; + + if (CountLabels(d) != DEVICE_INFO_RECORD_LABELS) + return mDNSfalse; + + // skip the instance name + afterInstance = SkipLeadingLabels(d, 1); + if (SameDomainName(afterInstance, &LocalDeviceInfoName)) + return mDNStrue; + + return mDNSfalse; +} + +// Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +// InterfaceID non-NULL tells us the interface this multicast response was received on +// InterfaceID NULL tells us this was a unicast response +// dstaddr NULL tells us we received this over an outgoing TCP connection we made mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, - const DNSMessage *const response, const mDNSu8 *end, - const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID) - { - int i; - - // 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 - } + const DNSMessage *const response, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + int i; + mDNSBool myself; + mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); + mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, &myself); + DNSQuestion *llqMatch = mDNSNULL; + DNSQuestion *unicastQuestion = mDNSNULL; + uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch); + + // "(CacheRecord*)1" is a special (non-zero) end-of-list marker + // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList + // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling. + CacheRecord *CacheFlushRecords = (CacheRecord*)1; + CacheRecord **cfp = &CacheFlushRecords; + CacheRecord *NSECRecords = mDNSNULL; + CacheRecord *NSECCachePtr = mDNSNULL; + CacheRecord **nsecp = &NSECRecords; + CacheRecord *McastNSEC3Records = mDNSNULL; + mDNSBool nseclist; + mDNSu8 rcode = '\0'; + mDNSBool rrsigsCreated = mDNSfalse; + mDNSBool DNSSECQuestion = mDNSfalse; + mDNSBool recordAccepted = mDNSfalse; + NetworkInterfaceInfo *llintf = FirstIPv4LLInterfaceForID(m, InterfaceID); + + // All records in a DNS response packet are treated as equally valid statements of truth. If we want + // to guard against spoof responses, then the only credible protection against that is cryptographic + // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record + int firstauthority = response->h.numAnswers; + int firstadditional = firstauthority + response->h.numAuthorities; + int totalrecords = firstadditional + response->h.numAdditionals; + const mDNSu8 *ptr = response->data; + DNSServer *uDNSServer = mDNSNULL; + + debugf("Received Response from %#-15a addressed to %#-15a on %p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes LLQType %d", + srcaddr, dstaddr, InterfaceID, + response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,", + response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", + response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", + response->h.numAdditionals, response->h.numAdditionals == 1 ? " " : "s", end - response->data, LLQType); + + // According to RFC 2181 <http://www.ietf.org/rfc/rfc2181.txt> + // When a DNS client receives a reply with TC + // set, it should ignore that response, and query again, using a + // mechanism, such as a TCP connection, that will permit larger replies. + // It feels wrong to be throwing away data after the network went to all the trouble of delivering it to us, but + // delivering some records of the RRSet first and then the remainder a couple of milliseconds later was causing + // failures in our Microsoft Active Directory client, which expects to get the entire set of answers at once. + // <rdar://problem/6690034> Can't bind to Active Directory + // In addition, if the client immediately canceled its query after getting the initial partial response, then we'll + // abort our TCP connection, and not complete the operation, and end up with an incomplete RRSet in our cache. + // Next time there's a query for this RRSet we'll see answers in our cache, and assume we have the whole RRSet already, + // and not even do the TCP query. + // Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the entire RRSet. + if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC)) return; + + if (LLQType == uDNS_LLQ_Ignore) return; + + // 1. We ignore questions (if any) in mDNS response packets + // 2. If this is an LLQ response, we handle it much the same + // 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this + // answer as being the authoritative complete RRSet, and respond by deleting all other + // matching cache records that don't appear in this packet. + // Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged + if (ResponseMCast || LLQType == uDNS_LLQ_Events || (response->h.flags.b[0] & kDNSFlag0_TC)) + ptr = LocateAnswers(response, end); + // Otherwise, for one-shot queries, any answers in our cache that are not also contained + // in this response packet are immediately deemed to be invalid. + else + { + mDNSBool failure, returnEarly; + rcode = (mDNSu8)(response->h.flags.b[1] & kDNSFlag1_RC_Mask); + failure = !(rcode == kDNSFlag1_RC_NoErr || rcode == kDNSFlag1_RC_NXDomain || rcode == kDNSFlag1_RC_NotAuth); + returnEarly = mDNSfalse; + // We could possibly combine this with the similar loop at the end of this function -- + // instead of tagging cache records here and then rescuing them if we find them in the answer section, + // we could instead use the "m->PktNum" mechanism to tag each cache record with the packet number in + // which it was received (or refreshed), and then at the end if we find any cache records which + // answer questions in this packet's question section, but which aren't tagged with this packet's + // packet number, then we deduce they are old and delete them + for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) + { + DNSQuestion q, *qptr = mDNSNULL; + ptr = getQuestion(response, ptr, end, InterfaceID, &q); + if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr))) + { + if (!failure) + { + CacheRecord *rr; + // Remember the unicast question that we found, which we use to make caching + // decisions later on in this function + const mDNSu32 slot = HashSlot(&q.qname); + CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); + if (!mDNSOpaque16IsZero(response->h.id)) + { + unicastQuestion = qptr; + if (qptr->qDNSServer && DNSSECQuestion(qptr)) + { + LogInfo("mDNSCoreReceiveResponse: Setting aware for %##s (%s) on %#a", qptr->qname.c, + DNSTypeName(qptr->qtype), &qptr->qDNSServer->addr); + qptr->qDNSServer->DNSSECAware = mDNStrue; + qptr->qDNSServer->req_DO = mDNStrue; + } + if (qptr->ValidatingResponse) + DNSSECQuestion = mDNStrue; + } + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) + { + debugf("uDNS marking %p %##s (%s) %p %s", q.InterfaceID, q.qname.c, DNSTypeName(q.qtype), + rr->resrec.InterfaceID, CRDisplayString(m, rr)); + // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm + rr->TimeRcvd = m->timenow - TicksTTL(rr) - 1; + rr->UnansweredQueries = MaxUnansweredQueries; + rr->CRDNSSECQuestion = 0; + if (unicastQuestion && DNSSECQuestion(unicastQuestion)) + { + LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for record %s, question %##s (%s)", CRDisplayString(m, rr), + unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype)); + rr->CRDNSSECQuestion = 1; + } + } + } + else + { + if (qptr) + { + // If we recv any error from the DNSServer for a DNSSEC Query and if we know that the server + // is not DNSSEC aware, stop doing DNSSEC for that DNSServer. Note that by setting the + // req_DO to false here, the next retransmission for this question will turn off validation + // and hence retransmit without the EDNS0/DOK option. + if (DNSSECOptionalQuestion(qptr) && qptr->qDNSServer && !qptr->qDNSServer->DNSSECAware) + { + LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to DNSSEC Query %##s (%s), clear DO flag", + qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); + qptr->qDNSServer->req_DO = mDNSfalse; + } + // For Unicast DNS Queries, penalize the DNSServer + else + { + LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", + qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); + PenalizeDNSServer(m, qptr, response->h.flags); + } + } + returnEarly = mDNStrue; + } + } + } + if (returnEarly) + { + LogInfo("Ignoring %2d Answer%s %2d Authorit%s %2d Additional%s", + response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", + response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", + response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); + // not goto exit because we won't have any CacheFlushRecords and we do not want to + // generate negative cache entries (we want to query the next server) + return; + } + if (unicastQuestion && DNSSECQuestion(unicastQuestion)) + { + BumpDNSSECStats(m, kStatsActionSet, kStatsTypeMsgSize, (end - response->data)); + } + } + + // Parse the NSEC3 records from the Authority section before we process + // the Answer section so that we can cache them along with the proper + // cache records we create. + if (mDNSOpaque16IsZero(response->h.id)) + mDNSParseNSEC3Records(m, response, end, InterfaceID, &McastNSEC3Records); + + for (i = 0; i < totalrecords && ptr && ptr < end; i++) + { + // All responses sent via LL multicast are acceptable for caching + // All responses received over our outbound TCP connections are acceptable for caching + mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType; + // (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer + // to any specific question -- any code reading records from the cache needs to make that determination for itself.) + + const mDNSu8 RecordType = + (i < firstauthority ) ? (mDNSu8)kDNSRecordTypePacketAns : + (i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd; + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec); + if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting + + if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) + { + mDNSCoreResetRecord(m); + continue; + } + + // We have already parsed the NSEC3 records and cached them approrpriately for + // multicast responses. + if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype == kDNSType_NSEC3) + { + mDNSCoreResetRecord(m); + continue; + } + // Don't want to cache OPT or TSIG pseudo-RRs + if (m->rec.r.resrec.rrtype == kDNSType_TSIG) + { + mDNSCoreResetRecord(m); + continue; + } + if (m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *opt; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently + // delete all our own AuthRecords (which are identified by having zero MAC tags on them). + for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++) + if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0]) + { + ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords); + ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords); + } + mDNSCoreResetRecord(m); + continue; + } + // if a CNAME record points to itself, then don't add it to the cache + if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name)) + { + LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c); + mDNSCoreResetRecord(m); + continue; + } + + // When we receive uDNS LLQ responses, we assume a long cache lifetime -- + // In the case of active LLQs, we'll get remove events when the records actually do go away + // In the case of polling LLQs, we assume the record remains valid until the next poll + if (!mDNSOpaque16IsZero(response->h.id)) + m->rec.r.resrec.rroriginalttl = GetEffectiveTTL(LLQType, m->rec.r.resrec.rroriginalttl); + + // If response was not sent via LL multicast, + // then see if it answers a recent query of ours, which would also make it acceptable for caching. + if (!ResponseMCast) + { + if (LLQType) + { + // For Long Lived queries that are both sent over UDP and Private TCP, LLQType is set. + // Even though it is AcceptableResponse, we need a matching DNSServer pointer for the + // queries to get ADD/RMV events. To lookup the question, we can't use + // ExpectingUnicastResponseForRecord as the port numbers don't match. uDNS_recvLLQRespose + // has already matched the question using the 64 bit Id in the packet and we use that here. + + if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer; + + // If this is a DNSSEC question that is also LongLived, don't accept records from the + // Additional/Authority section blindly. We need to go through IsAcceptableResponse below + // so that NSEC/NSEC3 record are cached in the nseclist if we accept them. This can happen + // for both negative responses and wildcard expanded positive responses as both of come + // back with NSEC/NSEC3s. + if (unicastQuestion && DNSSECQuestion(unicastQuestion)) + AcceptableResponse = mDNSfalse; + } + else if (!AcceptableResponse || !dstaddr) + { + // For responses that come over TCP (Responses that can't fit within UDP) or TLS (Private queries + // that are not long lived e.g., AAAA lookup in a Private domain), it is indicated by !dstaddr. + // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that + // we create. + + if (!mDNSOpaque16IsZero(response->h.id)) + { + DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); + + // Initialize the DNS server on the resource record which will now filter what questions we answer with + // this record. + // + // We could potentially lookup the DNS server based on the source address, but that may not work always + // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came + // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based + // on the "id" and "source port", then this response answers the question and assume the response + // came from the same DNS server that we sent the query to. + + if (q != mDNSNULL) + { + AcceptableResponse = mDNStrue; + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; + } + else + LogInfo("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + } + else + { + // If we can't find a matching question, we need to see whether we have seen records earlier that matched + // the question. The code below does that. So, make this record unacceptable for now + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); + AcceptableResponse = mDNSfalse; + } + } + } + else if (ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records)) + { + recordAccepted = mDNStrue; + AcceptableResponse = mDNStrue; + LogInfo("mDNSCoreReceiveResponse: Accepting record in response to QU question %s, InterfaceID %p", CRDisplayString(m, &m->rec.r), + InterfaceID); + } + else if (IsDeviceInfoRecord(m->rec.r.resrec.name)) + { + recordAccepted = mDNStrue; + AcceptableResponse = mDNStrue; + LogInfo("mDNSCoreReceiveResponse: Accepting _device-info record %s, InterfaceID %p", + CRDisplayString(m, &m->rec.r), InterfaceID); + } + } + } + else if (llintf && llintf->IgnoreIPv4LL && m->rec.r.resrec.rrtype == kDNSType_A) + { + CacheRecord *const rr = &m->rec.r; + RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; + + // If we are supposed to ignore link-local addresses on this interface, drop + // all "A" records that have link-local address in them. + if (mDNSv4AddressIsLinkLocal(&rdb->ipv4)) + { + LogInfo("mDNSResponder: Dropping LinkLocal packet %s", CRDisplayString(m, &m->rec.r)); + mDNSCoreResetRecord(m); + continue; + } + } + + // 1. Check that this packet resource record does not conflict with any of ours + if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype != kDNSType_NSEC) + { + if (m->CurrentRecord) + LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + // We accept all multicast responses, and unicast responses resulting from queries we issued + // For other unicast responses, this code accepts them only for responses with an + // (apparently) local source address that pertain to a record of our own that's in probing state + if (!AcceptableResponse && !(ResponseSrcLocal && rr->resrec.RecordType == kDNSRecordTypeUnique)) continue; + + if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match... + { + // ... check to see if type and rdata are identical + if (IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) + { + // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us + if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) + { + // If we were planning to send on this -- and only this -- interface, then we don't need to any more + if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; } + } + else + { + if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; } + else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } + } + } + // else, the packet RR has different type or different rdata -- check to see if this is a conflict + else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r)) + { + LogInfo("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogInfo("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr->resrec.rdatahash, ARDisplayString(m, rr)); + + // If this record is marked DependentOn another record for conflict detection purposes, + // then *that* record has to be bumped back to probing state to resolve the conflict + if (rr->DependentOn) + { + while (rr->DependentOn) rr = rr->DependentOn; + LogInfo("mDNSCoreReceiveResponse: Dep Record: %08lX %s", rr->resrec.rdatahash, ARDisplayString(m, rr)); + } + + // If we've just whacked this record's ProbeCount, don't need to do it again + if (rr->ProbeCount > DefaultProbeCountForTypeUnique) + LogInfo("mDNSCoreReceiveResponse: Already reset to Probing: %s", ARDisplayString(m, rr)); + else if (rr->ProbeCount == DefaultProbeCountForTypeUnique) + LogMsg("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr)); + else + { + LogMsg("mDNSCoreReceiveResponse: Received from %#a:%d %s", srcaddr, mDNSVal16(srcport), CRDisplayString(m, &m->rec.r)); + // If we'd previously verified this record, put it back to probing state and try again + if (rr->resrec.RecordType == kDNSRecordTypeVerified) + { + LogMsg("mDNSCoreReceiveResponse: Resetting to Probing: %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeUnique; + // We set ProbeCount to one more than the usual value so we know we've already touched this record. + // This is because our single probe for "example-name.local" could yield a response with (say) two A records and + // three AAAA records in it, and we don't want to call RecordProbeFailure() five times and count that as five conflicts. + // This special value is recognised and reset to DefaultProbeCountForTypeUnique in SendQueries(). + rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; + rr->AnnounceCount = InitialAnnounceCount; + InitializeLastAPTime(m, rr); + RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate + } + // If we're probing for this record, we just failed + else if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + // Before we call deregister, check if this is a packet we registered with the sleep proxy. + if (!mDNSCoreRegisteredProxyRecord(m, rr)) + { + LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr)); + + m->mDNSStats.NameConflicts++; + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + } + // We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the + // same machine giving different answers for the reverse mapping record, or there are two machines on the + // network using the same IP address.) This is simply a misconfiguration, and there's nothing we can do + // to fix it -- e.g. it's not our job to be trying to change the machine's IP address. We just discard our + // record to avoid continued conflicts (as we do for a conflict on our Unique records) and get on with life. + else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) + { + LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr)); + m->mDNSStats.KnownUniqueNameConflicts++; + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + else + LogMsg("mDNSCoreReceiveResponse: Unexpected record type %X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); + } + } + // Else, matching signature, different type or rdata, but not a considered a conflict. + // If the packet record has the cache-flush bit set, then we check to see if we + // have any record(s) of the same type that we should re-assert to rescue them + // (see note about "multi-homing and bridged networks" at the end of this function). + else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype) + if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) + { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } + } + } + } + + nseclist = mDNSfalse; + if (!AcceptableResponse) + { + AcceptableResponse = IsResponseAcceptable(m, CacheFlushRecords, unicastQuestion, &nseclist); + if (AcceptableResponse) m->rec.r.resrec.rDNSServer = uDNSServer; + } + + // 2. See if we want to add this packet resource record to our cache + // We only try to cache answers if we have a cache to put them in + // Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query + if (!AcceptableResponse) LogInfo("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r)); + if (m->rrcache_size && AcceptableResponse) + { + const mDNSu32 slot = HashSlot(m->rec.r.resrec.name); + CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); + CacheRecord *rr = mDNSNULL; + + if (McastNSEC3Records) + InitializeAnonInfoForCR(m, &McastNSEC3Records, &m->rec.r); + + // 2a. Check if this packet resource record is already in our cache. + // + // If this record should go in the nseclist, don't look in the cache for updating it. + // They are supposed to be cached under the "nsec" field of the cache record for + // validation. Just create the cache record. + if (!nseclist) + { + rr = mDNSCoreReceiveCacheCheck(m, response, LLQType, slot, cg, unicastQuestion, &cfp, &NSECCachePtr, InterfaceID); + } + + // If mDNSOppCaching is set (which affects only multicast), enable opportunistic caching in which case we cache + // everything that was received over multicast. Otherwise, we are selective about the caching. + // + // Cache everything that is from ourselves (that's how we answer any questions looking for them). Otherwise call + // ExpectingMulticastResponseForRecord which decides whether to cache this record or not. + // + if (!m->mDNSOppCaching && !rr && !myself && mDNSOpaque16IsZero(response->h.id)) + { + if (!ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records)) + { + //LogMsg("mDNSCoreReceiveResponse: discarding %s", CRDisplayString(m, &m->rec.r)); + mDNSCoreResetRecord(m); + continue; + } + else + { + recordAccepted = mDNStrue; + } + } + + + // If packet resource record not in our cache, add it now + // (unless it is just a deletion of a record we never had, in which case we don't care) + if (!rr && m->rec.r.resrec.rroriginalttl > 0) + { + const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events); + mDNSs32 delay; + + if (AddToCFList) + delay = NonZeroTime(m->timenow + mDNSPlatformOneSecond); + else + delay = CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot, mDNSNULL); + + // If unique, assume we may have to delay delivery of this 'add' event. + // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd() + // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime() + // to schedule an mDNS_Execute task at the appropriate time. + rr = CreateNewCacheEntry(m, slot, cg, delay, !nseclist, srcaddr); + if (rr) + { + rr->responseFlags = response->h.flags; + // If we are not creating signatures, then we need to inform DNSSEC so that + // it does not wait forever. Don't do this if we got NSEC records + // as it indicates that this name does not exist. + if (rr->resrec.rrtype == kDNSType_RRSIG && !nseclist) + { + rrsigsCreated = mDNStrue; + } + // Remember whether we created a cache record in response to a DNSSEC question. + // This helps DNSSEC code not to reissue the question to fetch the DNSSEC records. + rr->CRDNSSECQuestion = 0; + if (unicastQuestion && DNSSECQuestion(unicastQuestion)) + { + LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for new record %s, question %##s (%s)", CRDisplayString(m, rr), + unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype)); + rr->CRDNSSECQuestion = 1; + } + // NSEC/NSEC3 records and its signatures are cached with the negative cache entry + // which we should be creating below. It is also needed in the wildcard + // expanded answer case and in that case it is cached along with the answer. + if (nseclist) + { + rr->TimeRcvd = m->timenow; + *nsecp = rr; + nsecp = &rr->next; + } + else if (AddToCFList) + { + *cfp = rr; + cfp = &rr->NextInCFList; + *cfp = (CacheRecord*)1; + } + else if (rr->DelayDelivery) + { + ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery); + } + } + } + else + { + if (rr && rr->resrec.AnonInfo && m->rec.r.resrec.AnonInfo) + { + CopyAnonInfoForCR(m, rr, &m->rec.r); + } + } + } + mDNSCoreResetRecord(m); + } 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); - } - } - } + mDNSCoreResetRecord(m); + + // If we've just received one or more records with their cache flush bits set, + // then scan that cache slot to see if there are any old stale records we need to flush + while (CacheFlushRecords != (CacheRecord*)1) + { + CacheRecord *r1 = CacheFlushRecords, *r2; + const mDNSu32 slot = HashSlot(r1->resrec.name); + const CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec); + CacheFlushRecords = CacheFlushRecords->NextInCFList; + r1->NextInCFList = mDNSNULL; + + // Look for records in the cache with the same signature as this new one with the cache flush + // bit set, and either (a) if they're fresh, just make sure the whole RRSet has the same TTL + // (as required by DNS semantics) or (b) if they're old, mark them for deletion in one second. + // We make these TTL adjustments *only* for records that still have *more* than one second + // remaining to live. Otherwise, a record that we tagged for deletion half a second ago + // (and now has half a second remaining) could inadvertently get its life extended, by either + // (a) if we got an explicit goodbye packet half a second ago, the record would be considered + // "fresh" and would be incorrectly resurrected back to the same TTL as the rest of the RRSet, + // or (b) otherwise, the record would not be fully resurrected, but would be reset to expire + // in one second, thereby inadvertently delaying its actual expiration, instead of hastening it. + // If this were to happen repeatedly, the record's expiration could be deferred indefinitely. + // To avoid this, we need to ensure that the cache flushing operation will only act to + // *decrease* a record's remaining lifetime, never *increase* it. + for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next) + { + mDNSu16 id1; + mDNSu16 id2; + if (!r1->resrec.InterfaceID) + { + id1 = (r1->resrec.rDNSServer ? r1->resrec.rDNSServer->resGroupID : 0); + id2 = (r2->resrec.rDNSServer ? r2->resrec.rDNSServer->resGroupID : 0); + } + else + { + id1 = id2 = 0; + } + // When we receive new RRSIGs e.g., for DNSKEY record, we should not flush the old + // RRSIGS e.g., for TXT record. To do so, we need to look at the typeCovered field of + // the new RRSIG that we received. Process only if the typeCovered matches. + if ((r1->resrec.rrtype == r2->resrec.rrtype) && (r1->resrec.rrtype == kDNSType_RRSIG)) + { + rdataRRSig *rrsig1 = (rdataRRSig *)(((RDataBody2 *)(r1->resrec.rdata->u.data))->data); + rdataRRSig *rrsig2 = (rdataRRSig *)(((RDataBody2 *)(r2->resrec.rdata->u.data))->data); + if (swap16(rrsig1->typeCovered) != swap16(rrsig2->typeCovered)) + { + debugf("mDNSCoreReceiveResponse: Received RRSIG typeCovered %s, found %s, not processing", + DNSTypeName(swap16(rrsig1->typeCovered)), DNSTypeName(swap16(rrsig2->typeCovered))); + continue; + } + } + + // For Unicast (null InterfaceID) the resolver IDs should also match + if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) && + (r1->resrec.InterfaceID || (id1 == id2)) && + r1->resrec.rrtype == r2->resrec.rrtype && + r1->resrec.rrclass == r2->resrec.rrclass) + { + // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics) + // else, if record is old, mark it to be flushed + if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) + { + // If we find mismatched TTLs in an RRSet, correct them. + // We only do this for records with a TTL of 2 or higher. It's possible to have a + // goodbye announcement with the cache flush bit set (or a case-change on record rdata, + // which we treat as a goodbye followed by an addition) and in that case it would be + // inappropriate to synchronize all the other records to a TTL of 0 (or 1). + // We suppress the message for the specific case of correcting from 240 to 60 for type TXT, + // because certain early Bonjour devices are known to have this specific mismatch, and + // there's no point filling syslog with messages about something we already know about. + // We also don't log this for uDNS responses, since a caching name server is obliged + // to give us an aged TTL to correct for how long it has held the record, + // so our received TTLs are expected to vary in that case + if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1) + { + if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) && + mDNSOpaque16IsZero(response->h.id)) + LogInfo("Correcting TTL from %4d to %4d for %s", + r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2)); + r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; + } + r2->TimeRcvd = m->timenow; + } + else // else, if record is old, mark it to be flushed + { + verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1)); + verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2)); + // We set stale records to expire in one second. + // This gives the owner a chance to rescue it if necessary. + // This is important in the case of multi-homing and bridged networks: + // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be + // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit + // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet + // will promptly delete their cached copies of the (still valid) Ethernet IP address record. + // By delaying the deletion by one second, we give X a change to notice that this bridging has + // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. + + // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary + // final expiration queries for this record. + + // If a record is deleted twice, first with an explicit DE record, then a second time by virtue of the cache + // flush bit on the new record replacing it, then we allow the record to be deleted immediately, without the usual + // one-second grace period. This improves responsiveness for mDNS_Update(), as used for things like iChat status updates. + // <rdar://problem/5636422> Updating TXT records is too slow + // We check for "rroriginalttl == 1" because we want to include records tagged by the "packet TTL is zero" check above, + // which sets rroriginalttl to 1, but not records tagged by the rdata case-change check, which sets rroriginalttl to 0. + if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl == 1 && r2->UnansweredQueries == MaxUnansweredQueries) + { + LogInfo("Cache flush for DE record %s", CRDisplayString(m, r2)); + r2->resrec.rroriginalttl = 0; + } + else if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) + { + // We only set a record to expire in one second if it currently has *more* than a second to live + // If it's already due to expire in a second or less, we just leave it alone + r2->resrec.rroriginalttl = 1; + r2->UnansweredQueries = MaxUnansweredQueries; + r2->TimeRcvd = m->timenow - 1; + // We use (m->timenow - 1) instead of m->timenow, because we use that to identify records + // that we marked for deletion via an explicit DE record + } + } + SetNextCacheCheckTimeForRecord(m, r2); + } + } + + if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to + { + // If we had a unicast question for this response with at least one positive answer and we + // have NSECRecords, it is most likely a wildcard expanded answer. Cache the NSEC and its + // signatures along with the cache record which will be used for validation later. If + // we rescued a few records earlier in this function, then NSECCachePtr would be set. In that + // use that instead. + if (response->h.numAnswers && unicastQuestion && NSECRecords) + { + if (!NSECCachePtr) + { + LogInfo("mDNSCoreReceiveResponse: Updating NSECCachePtr to %s", CRDisplayString(m, r1)); + NSECCachePtr = r1; + } + // Note: We need to do this before we call CacheRecordDeferredAdd as this + // might start the verification process which needs these NSEC records + if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) + { + LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); + FreeNSECRecords(m, NSECRecords); + } + NSECRecords = mDNSNULL; + NSECCachePtr = mDNSNULL; + } + r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot, mDNSNULL); + // If no longer delaying, deliver answer now, else schedule delivery for the appropriate time + if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); + else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery); + } + } + + // If we have not consumed the NSEC records yet e.g., just refreshing the cache, + // update them now for future validations. + if (NSECRecords && NSECCachePtr) + { + LogInfo("mDNSCoreReceieveResponse: Updating NSEC records in %s", CRDisplayString(m, NSECCachePtr)); + if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) + { + LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); + FreeNSECRecords(m, NSECRecords); + } + NSECRecords = mDNSNULL; + NSECCachePtr = mDNSNULL; + } + + // If there is at least one answer and we did not create RRSIGs and there was a + // ValidatingResponse question waiting for this response, give a hint that no RRSIGs + // were created. We don't need to give a hint: + // + // - if we have no answers, the mDNSCoreReceiveNoUnicastAnswers below should + // generate a negative response + // + // - if we have NSECRecords, it means we might have a potential proof for + // non-existence of name that we are looking for + // + if (response->h.numAnswers && !rrsigsCreated && DNSSECQuestion && !NSECRecords) + mDNSCoreReceiveNoDNSSECAnswers(m, response, end, dstaddr, dstport, InterfaceID); + + // See if we need to generate negative cache entries for unanswered unicast questions + mDNSCoreReceiveNoUnicastAnswers(m, response, end, dstaddr, dstport, InterfaceID, LLQType, rcode, NSECRecords); + + if (McastNSEC3Records) + { + debugf("mDNSCoreReceiveResponse: McastNSEC3Records not used"); + FreeNSECRecords(m, McastNSEC3Records); + } +} + +// ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing +// multiple wakeup magic packets to be sent if appropriate, and all records to be ultimately freed after a few seconds. +// ScheduleWakeup is called on mDNS record conflicts, ARP conflicts, NDP conflicts, or reception of trigger traffic +// that warrants waking the sleeping host. +// ScheduleWakeup must be called with the lock held (ScheduleWakeupForList uses mDNS_Deregister_internal) + +mDNSlocal void ScheduleWakeupForList(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e, AuthRecord *const thelist) +{ + // We need to use the m->CurrentRecord mechanism here when dealing with DuplicateRecords list as + // mDNS_Deregister_internal deregisters duplicate records immediately as they are not used + // to send wakeups or goodbyes. See the comment in that function for more details. To keep it + // simple, we use the same mechanism for both lists. + if (!e->l[0]) + { + LogMsg("ScheduleWakeupForList ERROR: Target HMAC is zero"); + return; + } + m->CurrentRecord = thelist; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && mDNSSameEthAddress(&rr->WakeUp.HMAC, e)) + { + LogInfo("ScheduleWakeupForList: Scheduling wakeup packets for %s", ARDisplayString(m, rr)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + } + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} + +mDNSlocal void ScheduleWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e) +{ + if (!e->l[0]) { LogMsg("ScheduleWakeup ERROR: Target HMAC is zero"); return; } + ScheduleWakeupForList(m, InterfaceID, e, m->DuplicateRecords); + ScheduleWakeupForList(m, InterfaceID, e, m->ResourceRecords); +} + +mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus result) +{ + if (result && result != mStatus_MemFree) + LogInfo("SPS Callback %d %s", result, ARDisplayString(m, ar)); + + if (result == mStatus_NameConflict) + { + mDNS_Lock(m); + LogMsg("%-7s Conflicting mDNS -- waking %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); + if (ar->WakeUp.HMAC.l[0]) + { + SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password); // Send one wakeup magic packet + ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); // Schedule all other records with the same owner to be woken + } + mDNS_Unlock(m); + } + + if (result == mStatus_NameConflict || result == mStatus_MemFree) + { + m->ProxyRecords--; + mDNSPlatformMemFree(ar); + mDNS_UpdateAllowSleep(m); + } +} + +mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth) +{ + int i; + mDNSs8 hval = 0; + int colons = 0; + mDNSu8 val = 0; + + for (i = 0; ptr < limit && *ptr != ' ' && i < 17; i++, ptr++) + { + hval = HexVal(*ptr); + if (hval != -1) + { + val <<= 4; + val |= hval; + } + else if (*ptr == ':') + { + eth->b[colons] = val; + colons++; + val = 0; + } + } + if (colons != 5) + { + LogMsg("GetValueForMACAddr: Address malformed colons %d", colons); + return mDNSNULL; + } + eth->b[colons] = val; + return ptr; +} + +mDNSlocal mDNSu8 *GetValueForIPv6Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv6Addr *v6) +{ + int hval; + int value; + int numBytes; + int digitsProcessed; + int zeroFillStart; + int numColons; + mDNSu8 v6addr[16]; + + // RFC 3513: Section 2.2 specifies IPv6 presentation format. The following parsing + // handles both (1) and (2) and does not handle embedded IPv4 addresses. + // + // First forms a address in "v6addr", then expands to fill the zeroes in and returns + // the result in "v6" + + numColons = numBytes = value = digitsProcessed = zeroFillStart = 0; + while (ptr < limit && *ptr != ' ') + { + hval = HexVal(*ptr); + if (hval != -1) + { + value <<= 4; + value |= hval; + digitsProcessed = 1; + } + else if (*ptr == ':') + { + if (!digitsProcessed) + { + // If we have already seen a "::", we should not see one more. Handle the special + // case of "::" + if (numColons) + { + // if we never filled any bytes and the next character is space (we have reached the end) + // we are done + if (!numBytes && (ptr + 1) < limit && *(ptr + 1) == ' ') + { + mDNSPlatformMemZero(v6->b, 16); + return ptr + 1; + } + LogMsg("GetValueForIPv6Addr: zeroFillStart non-zero %d", zeroFillStart); + return mDNSNULL; + } + + // We processed "::". We need to fill zeroes later. For now, mark the + // point where we will start filling zeroes from. + zeroFillStart = numBytes; + numColons++; + } + else if ((ptr + 1) < limit && *(ptr + 1) == ' ') + { + // We have a trailing ":" i.e., no more characters after ":" + LogMsg("GetValueForIPv6Addr: Trailing colon"); + return mDNSNULL; + } + else + { + // For a fully expanded IPv6 address, we fill the 14th and 15th byte outside of this while + // loop below as there is no ":" at the end. Hence, the last two bytes that can possibly + // filled here is 12 and 13. + if (numBytes > 13) { LogMsg("GetValueForIPv6Addr:1: numBytes is %d", numBytes); return mDNSNULL; } + + v6addr[numBytes++] = (mDNSu8) ((value >> 8) & 0xFF); + v6addr[numBytes++] = (mDNSu8) (value & 0xFF); + digitsProcessed = value = 0; + + // Make sure that we did not fill the 13th and 14th byte above + if (numBytes > 14) { LogMsg("GetValueForIPv6Addr:2: numBytes is %d", numBytes); return mDNSNULL; } + } + } + ptr++; + } + + // We should be processing the last set of bytes following the last ":" here + if (!digitsProcessed) + { + LogMsg("GetValueForIPv6Addr: no trailing bytes after colon, numBytes is %d", numBytes); + return mDNSNULL; + } + + if (numBytes > 14) { LogMsg("GetValueForIPv6Addr:3: numBytes is %d", numBytes); return mDNSNULL; } + v6addr[numBytes++] = (mDNSu8) ((value >> 8) & 0xFF); + v6addr[numBytes++] = (mDNSu8) (value & 0xFF); + + if (zeroFillStart) + { + int i, j, n; + for (i = 0; i < zeroFillStart; i++) + v6->b[i] = v6addr[i]; + for (j = i, n = 0; n < 16 - numBytes; j++, n++) + v6->b[j] = 0; + for (; j < 16; i++, j++) + v6->b[j] = v6addr[i]; + } + else if (numBytes == 16) + mDNSPlatformMemCopy(v6->b, v6addr, 16); + else + { + LogMsg("GetValueForIPv6addr: Not enough bytes for IPv6 address, numBytes is %d", numBytes); + return mDNSNULL; + } + return ptr; +} + +mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4) +{ + mDNSu32 val; + int dots = 0; + val = 0; + + for ( ; ptr < limit && *ptr != ' '; ptr++) + { + if (*ptr >= '0' && *ptr <= '9') + val = val * 10 + *ptr - '0'; + else if (*ptr == '.') + { + v4->b[dots++] = val; + val = 0; + } + else + { + // We have a zero at the end and if we reached that, then we are done. + if (*ptr == 0 && ptr == limit - 1 && dots == 3) + { + v4->b[dots] = val; + return ptr + 1; + } + else { LogMsg("GetValueForIPv4Addr: something wrong ptr(%p) %c, limit %p, dots %d", ptr, *ptr, limit, dots); return mDNSNULL; } + } + } + if (dots != 3) { LogMsg("GetValueForIPv4Addr: Address malformed dots %d", dots); return mDNSNULL; } + v4->b[dots] = val; + return ptr; +} + +mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *value) +{ + mDNSu32 val; + + val = 0; + for ( ; ptr < limit && *ptr != ' '; ptr++) + { + if (*ptr < '0' || *ptr > '9') + { + // We have a zero at the end and if we reached that, then we are done. + if (*ptr == 0 && ptr == limit - 1) + { + *value = val; + return ptr + 1; + } + else { LogMsg("GetValueForKeepalive: *ptr %d, ptr %p, limit %p, ptr +1 %d", *ptr, ptr, limit, *(ptr + 1)); return mDNSNULL; } + } + val = val * 10 + *ptr - '0'; + } + *value = val; + return ptr; +} + +mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, mDNSu32 *seq, + mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win) +{ + if (ar->resrec.rrtype != kDNSType_NULL) + return; + + if (mDNS_KeepaliveRecord(&ar->resrec)) + { + int len = ar->resrec.rdlength; + mDNSu8 *ptr = &ar->resrec.rdata->u.txt.c[1]; + mDNSu8 *limit = ptr + len - 1; // Exclude the first byte that is the length + mDNSu32 value = 0; + + while (ptr < limit) + { + mDNSu8 param = *ptr; + ptr += 2; // Skip the letter and the "=" + if (param == 'h') + { + laddr->type = mDNSAddrType_IPv4; + ptr = GetValueForIPv4Addr(ptr, limit, &laddr->ip.v4); + } + else if (param == 'd') + { + raddr->type = mDNSAddrType_IPv4; + ptr = GetValueForIPv4Addr(ptr, limit, &raddr->ip.v4); + } + if (param == 'H') + { + laddr->type = mDNSAddrType_IPv6; + ptr = GetValueForIPv6Addr(ptr, limit, &laddr->ip.v6); + } + else if (param == 'D') + { + raddr->type = mDNSAddrType_IPv6; + ptr = GetValueForIPv6Addr(ptr, limit, &raddr->ip.v6); + } + else if (param == 'm') + { + ptr = GetValueForMACAddr(ptr, limit, eth); + } + else + { + ptr = GetValueForKeepalive(ptr, limit, &value); + } + if (!ptr) { LogMsg("mDNS_ExtractKeepaliveInfo: Cannot parse\n"); return; } + + // Extract everything in network order so that it is easy for sending a keepalive and also + // for matching incoming TCP packets + switch (param) + { + case 't': + *timeout = value; + //if (*timeout < 120) *timeout = 120; + break; + case 'h': + case 'H': + case 'd': + case 'D': + case 'm': + case 'i': + case 'c': + break; + case 'l': + lport->NotAnInteger = swap16((mDNSu16)value); + break; + case 'r': + rport->NotAnInteger = swap16((mDNSu16)value); + break; + case 's': + *seq = swap32(value); + break; + case 'a': + *ack = swap32(value); + break; + case 'w': + *win = swap16((mDNSu16)value); + break; + default: + LogMsg("mDNS_ExtractKeepaliveInfo: unknown value %c\n", param); + ptr = limit; + break; + } + ptr++; // skip the space + } + } +} + +// Matches the proxied auth records to the incoming TCP packet and returns the match and its sequence and ack in "rseq" and "rack" so that +// the clients need not retrieve this information from the auth record again. +mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr* pladdr, const mDNSAddr* praddr, const mDNSIPPort plport, + const mDNSIPPort prport, mDNSu32 *rseq, mDNSu32 *rack) +{ + AuthRecord *ar; + mDNSAddr laddr, raddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; + + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + timeout = seq = ack = 0; + win = 0; + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + + if (!ar->WakeUp.HMAC.l[0]) continue; + + mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); + + // Did we parse correctly ? + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win) + { + debugf("mDNS_MatchKeepaliveInfo: not a valid record %s for keepalive", ARDisplayString(m, ar)); + continue; + } + + debugf("mDNS_MatchKeepaliveInfo: laddr %#a pladdr %#a, raddr %#a praddr %#a, lport %d plport %d, rport %d prport %d", + &laddr, pladdr, &raddr, praddr, mDNSVal16(lport), mDNSVal16(plport), mDNSVal16(rport), mDNSVal16(prport)); + + // Does it match the incoming TCP packet ? + if (mDNSSameAddress(&laddr, pladdr) && mDNSSameAddress(&raddr, praddr) && mDNSSameIPPort(lport, plport) && mDNSSameIPPort(rport, prport)) + { + // returning in network order + *rseq = seq; + *rack = ack; + return ar; + } + } + return mDNSNULL; +} + +mDNSlocal void mDNS_SendKeepalives(mDNS *const m) +{ + AuthRecord *ar; + + for (ar = m->ResourceRecords; ar; ar=ar->next) + { + mDNSu32 timeout, seq, ack; + mDNSu16 win; + mDNSAddr laddr, raddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + + timeout = seq = ack = 0; + win = 0; + + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + + if (!ar->WakeUp.HMAC.l[0]) continue; + + mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); + + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win) + { + debugf("mDNS_SendKeepalives: not a valid record %s for keepalive", ARDisplayString(m, ar)); + continue; + } + LogMsg("mDNS_SendKeepalives: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport)); + + // When we receive a proxy update, we set KATimeExpire to zero so that we always send a keepalive + // immediately (to detect any potential problems). After that we always set it to a non-zero value. + if (!ar->KATimeExpire || (m->timenow - ar->KATimeExpire >= 0)) + { + mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win); + ar->KATimeExpire = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); + } + if (m->NextScheduledKA - ar->KATimeExpire > 0) + m->NextScheduledKA = ar->KATimeExpire; + } +} + +mDNSlocal void mDNS_SendKeepaliveACK(mDNS *const m, AuthRecord *ar) +{ + if (ar != mDNSNULL) + { + LogInfo("mDNS_SendKeepalivesACK: AuthRecord is NULL"); + return; + } + mDNSu32 timeout, seq, ack; + mDNSu16 win; + mDNSAddr laddr, raddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + + timeout = seq = ack = 0; + win = 0; + + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + + mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); + + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win) + { + LogInfo("mDNS_SendKeepaliveACK: not a valid record %s for keepalive", ARDisplayString(m, ar)); + return; + } + LogMsg("mDNS_SendKeepaliveACK: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport)); + mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win); +} + +mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, + const DNSMessage *const msg, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + int i; + AuthRecord opt; + mDNSu8 *p = m->omsg.data; + OwnerOptData owner = zeroOwner; // Need to zero this, so we'll know if this Update packet was missing its Owner option + mDNSu32 updatelease = 0; + const mDNSu8 *ptr; + + LogSPS("Received Update from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); + + if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return; + + if (mDNS_PacketLoggingEnabled) + DumpPacket(m, mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); + + ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space + DNSOpt_OwnerData_ID_Space); + if (ptr) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *o; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++) + { + if (o->opt == kDNSOpt_Lease) updatelease = o->u.updatelease; + else if (o->opt == kDNSOpt_Owner && o->u.owner.vers == 0) owner = o->u.owner; + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + InitializeDNSMessage(&m->omsg.h, msg->h.id, UpdateRespFlags); + + if (!updatelease || !owner.HMAC.l[0]) + { + static int msgs = 0; + if (msgs < 100) + { + msgs++; + LogMsg("Refusing sleep proxy registration from %#a:%d:%s%s", srcaddr, mDNSVal16(srcport), + !updatelease ? " No lease" : "", !owner.HMAC.l[0] ? " No owner" : ""); + } + m->omsg.h.flags.b[1] |= kDNSFlag1_RC_FormErr; + } + else if (m->ProxyRecords + msg->h.mDNS_numUpdates > MAX_PROXY_RECORDS) + { + static int msgs = 0; + if (msgs < 100) + { + msgs++; + LogMsg("Refusing sleep proxy registration from %#a:%d: Too many records %d + %d = %d > %d", srcaddr, mDNSVal16(srcport), + m->ProxyRecords, msg->h.mDNS_numUpdates, m->ProxyRecords + msg->h.mDNS_numUpdates, MAX_PROXY_RECORDS); + } + m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; + } + else + { + LogSPS("Received Update for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); + + if (updatelease > 24 * 60 * 60) + updatelease = 24 * 60 * 60; + + if (updatelease > 0x40000000UL / mDNSPlatformOneSecond) + updatelease = 0x40000000UL / mDNSPlatformOneSecond; + + ptr = LocateAuthorities(msg, end); + + // Clear any stale TCP keepalive records that may exist + ClearKeepaliveProxyRecords(m, &owner, m->DuplicateRecords, InterfaceID); + ClearKeepaliveProxyRecords(m, &owner, m->ResourceRecords, InterfaceID); + + for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) + { + mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec); + AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem); + if (!ar) + { + m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; + break; + } + else + { + mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared; + m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet; + // All stale keepalive records have been flushed prior to this loop. + if (!mDNS_KeepaliveRecord(&m->rec.r.resrec)) + { + ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords); // Make sure we don't have any old stale duplicates of this record + ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords); + } + mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, AuthRecordAny, SPSRecordCallback, ar); + AssignDomainName(&ar->namestorage, m->rec.r.resrec.name); + ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse); + ar->resrec.rdata->MaxRDLength = RDLengthMem; + mDNSPlatformMemCopy(ar->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, RDLengthMem); + ar->ForceMCast = mDNStrue; + ar->WakeUp = owner; + if (m->rec.r.resrec.rrtype == kDNSType_PTR) + { + mDNSs32 t = ReverseMapDomainType(m->rec.r.resrec.name); + if (t == mDNSAddrType_IPv4) GetIPv4FromName(&ar->AddressProxy, m->rec.r.resrec.name); + else if (t == mDNSAddrType_IPv6) GetIPv6FromName(&ar->AddressProxy, m->rec.r.resrec.name); + debugf("mDNSCoreReceiveUpdate: PTR %d %d %#a %s", t, ar->AddressProxy.type, &ar->AddressProxy, ARDisplayString(m, ar)); + if (ar->AddressProxy.type) SetSPSProxyListChanged(InterfaceID); + } + ar->TimeRcvd = m->timenow; + ar->TimeExpire = m->timenow + updatelease * mDNSPlatformOneSecond; + if (m->NextScheduledSPS - ar->TimeExpire > 0) + m->NextScheduledSPS = ar->TimeExpire; + ar->KATimeExpire = 0; + mDNS_Register_internal(m, ar); + + m->ProxyRecords++; + mDNS_UpdateAllowSleep(m); + LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar)); + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + if (m->omsg.h.flags.b[1] & kDNSFlag1_RC_Mask) + { + LogMsg("Refusing sleep proxy registration from %#a:%d: Out of memory", srcaddr, mDNSVal16(srcport)); + ClearProxyRecords(m, &owner, m->DuplicateRecords); + ClearProxyRecords(m, &owner, m->ResourceRecords); + } + else + { + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + opt.resrec.rdata->u.opt[0].u.updatelease = updatelease; + p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); + } + } + + if (p) mDNSSendDNSMessage(m, &m->omsg, p, InterfaceID, m->SPSSocket, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + mDNS_SendKeepalives(m); +} + +mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID) +{ + if (InterfaceID) + { + mDNSu32 updatelease = 60 * 60; // If SPS fails to indicate lease time, assume one hour + const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space); + if (ptr) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *o; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++) + if (o->opt == kDNSOpt_Lease) + { + updatelease = o->u.updatelease; + LogSPS("Sleep Proxy granted lease time %4d seconds, updateid %d, InterfaceID %p", updatelease, mDNSVal16(msg->h.id), InterfaceID); + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + if (m->CurrentRecord) + LogMsg("mDNSCoreReceiveUpdateR ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (rr->resrec.InterfaceID == InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name)))) + if (mDNSSameOpaque16(rr->updateid, msg->h.id)) + { + // We successfully completed this record's registration on this "InterfaceID". Clear that bit. + // Clear the updateid when we are done sending on all interfaces. + mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); + if (scopeid < (sizeof(rr->updateIntID) * mDNSNBBY)) + bit_clr_opaque64(rr->updateIntID, scopeid); + if (mDNSOpaque64IsZero(&rr->updateIntID)) + rr->updateid = zeroID; + rr->expire = NonZeroTime(m->timenow + updatelease * mDNSPlatformOneSecond); + LogSPS("Sleep Proxy %s record %5d 0x%x 0x%x (%d) %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", updatelease, rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr)); + if (rr->WakeUp.HMAC.l[0]) + { + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + } + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } + + // Update the dynamic store with the IP Address and MAC address of the sleep proxy + char *ifname = InterfaceNameForID(m, InterfaceID); + mDNSAddr spsaddr; + mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr)); + mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname); + } + // If we were waiting to go to sleep, then this SPS registration or wide-area record deletion + // may have been the thing we were waiting for, so schedule another check to see if we can sleep now. + if (m->SleepLimit) m->NextScheduledSPRetry = m->timenow; +} + +mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, + const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver) +{ + if (cr == &m->rec.r && m->rec.r.resrec.RecordType) + { + LogMsg("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); +#if ForceAlerts + *(long*)0 = 0; +#endif + } + + // Create empty resource record + cr->resrec.RecordType = kDNSRecordTypePacketNegative; + cr->resrec.InterfaceID = InterfaceID; + cr->resrec.rDNSServer = dnsserver; + cr->resrec.name = name; // Will be updated to point to cg->name when we call CreateNewCacheEntry + cr->resrec.rrtype = rrtype; + cr->resrec.rrclass = rrclass; + cr->resrec.rroriginalttl = ttl_seconds; + cr->resrec.rdlength = 0; + cr->resrec.rdestimate = 0; + cr->resrec.namehash = namehash; + cr->resrec.rdatahash = 0; + cr->resrec.rdata = (RData*)&cr->smallrdatastorage; + cr->resrec.rdata->MaxRDLength = 0; + + cr->NextInKAList = mDNSNULL; + cr->TimeRcvd = m->timenow; + cr->DelayDelivery = 0; + cr->NextRequiredQuery = m->timenow; + cr->LastUsed = m->timenow; + cr->CRActiveQuestion = mDNSNULL; + cr->UnansweredQueries = 0; + cr->LastUnansweredTime = 0; +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + cr->MPUnansweredQ = 0; + cr->MPLastUnansweredQT = 0; + cr->MPUnansweredKA = 0; + cr->MPExpectingKA = mDNSfalse; +#endif + cr->NextInCFList = mDNSNULL; + cr->nsec = mDNSNULL; + cr->soa = mDNSNULL; + cr->CRDNSSECQuestion = 0; + // Initialize to the basic one and the caller can set it to more + // specific based on the response if any + cr->responseFlags = ResponseFlags; +} 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; } + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + mDNSInterfaceID ifid = InterfaceID; + DNSMessage *msg = (DNSMessage *)pkt; + const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; + const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + const mDNSu8 UpdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_Update; + const mDNSu8 UpdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + mDNSu8 QR_OP; + mDNSu8 *ptr = mDNSNULL; + mDNSBool TLS = (dstaddr == (mDNSAddr *)1); // For debug logs: dstaddr = 0 means TCP; dstaddr = 1 means TLS + if (TLS) dstaddr = mDNSNULL; + +#ifndef UNICAST_DISABLED + if (mDNSSameAddress(srcaddr, &m->Router)) + { +#ifdef _LEGACY_NAT_TRAVERSAL_ + if (mDNSSameIPPort(srcport, SSDPPort) || (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port))) + { + mDNS_Lock(m); + LNT_ConfigureRouterInfo(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + mDNS_Unlock(m); + return; + } +#endif + if (mDNSSameIPPort(srcport, NATPMPPort)) + { + mDNS_Lock(m); + uDNS_ReceiveNATPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + mDNS_Unlock(m); + return; + } + } +#ifdef _LEGACY_NAT_TRAVERSAL_ + else if (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port)) { debugf("Ignoring SSDP response from %#a:%d", srcaddr, mDNSVal16(srcport)); return; } +#endif + +#endif + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) + { + LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + return; + } + QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + // Read the integer parts which are in IETF byte-order (MSB first, LSB second) + ptr = (mDNSu8 *)&msg->h.numQuestions; + msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + + if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; } + + // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address" + // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up + if (srcaddr && !mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } + + mDNS_Lock(m); + m->PktNum++; + if (mDNSOpaque16IsZero(msg->h.id)) + { + m->MPktNum++; +#if APPLE_OSX_mDNSResponder + // Track the number of multicast packets received from a source outside our subnet. + // Check the destination address to avoid accounting for spurious packets that + // comes in with message id zero. + if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL) && + mDNSAddressIsAllDNSLinkGroup(dstaddr)) + { + m->RemoteSubnet++; + } +#endif // #if APPLE_OSX_mDNSResponder + } - 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 (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR))) + if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses + { + ifid = mDNSInterface_Any; + if (mDNS_PacketLoggingEnabled) + DumpPacket(m, mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); + uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport); + // Note: mDNSCore also needs to get access to received unicast responses + } +#endif + if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid); + else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid); + else if (QR_OP == UpdQ) mDNSCoreReceiveUpdate (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end, srcaddr, InterfaceID); + else + { + LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)", + msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID); + if (mDNS_LoggingEnabled) + { + int i = 0; + while (i<end - (mDNSu8 *)pkt) + { + char buffer[128]; + char *p = buffer + mDNS_snprintf(buffer, sizeof(buffer), "%04X", i); + do if (i<end - (mDNSu8 *)pkt) p += mDNS_snprintf(p, sizeof(buffer), " %02X", ((mDNSu8 *)pkt)[i]);while (++i & 15); + LogInfo("%s", buffer); + } + } + } + // Packet reception often causes a change to the task list: + // 1. Inbound queries can cause us to need to send responses + // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses + // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records + // 4. Response packets that answer questions may cause our client to issue new questions + mDNS_Unlock(m); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - -#pragma mark - #pragma mark - Searcher Functions #endif -#define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger) +// Targets are considered the same if both queries are untargeted, or +// if both are targeted to the same address+port +// (If Target address is zero, TargetPort is undefined) +#define SameQTarget(A,B) (((A)->Target.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \ + (mDNSSameAddress(& (A)->Target, & (B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort))) + +// Note: We explicitly disallow making a public query be a duplicate of a private one. This is to avoid the +// circular deadlock where a client does a query for something like "dns-sd -Q _dns-query-tls._tcp.company.com SRV" +// and we have a key for company.com, so we try to locate the private query server for company.com, which necessarily entails +// doing a standard DNS query for the _dns-query-tls._tcp SRV record for company.com. If we make the latter (public) query +// a duplicate of the former (private) query, then it will block forever waiting for an answer that will never come. +// +// We keep SuppressUnusable questions separate so that we can return a quick response to them and not get blocked behind +// the queries that are not marked SuppressUnusable. But if the query is not suppressed, they are treated the same as +// non-SuppressUnusable questions. This should be fine as the goal of SuppressUnusable is to return quickly only if it +// is suppressed. If it is not suppressed, we do try all the DNS servers for valid answers like any other question. +// The main reason for this design is that cache entries point to a *single* question and that question is responsible +// for keeping the cache fresh as long as it is active. Having multiple active question for a single cache entry +// breaks this design principle. +// + +// If IsLLQ(Q) is true, it means the question is both: +// (a) long-lived and +// (b) being performed by a unicast DNS long-lived query (either full LLQ, or polling) +// for multicast questions, we don't want to treat LongLived as anything special +#define IsLLQ(Q) ((Q)->LongLived && !mDNSOpaque16IsZero((Q)->TargetQID)) +#define IsAWDLIncluded(Q) (((Q)->flags & kDNSServiceFlagsIncludeAWDL) != 0) 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); - } - } +{ + DNSQuestion *q; + // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list. + // This prevents circular references, where two questions are each marked as a duplicate of the other. + // Accordingly, we break out of the loop when we get to 'question', because there's no point searching + // further in the list. + for (q = m->Questions; q && q != question; q=q->next) // Scan our list for another question + if (q->InterfaceID == question->InterfaceID && // with the same InterfaceID, + SameQTarget(q, question) && // and same unicast/multicast target settings + q->qtype == question->qtype && // type, + q->qclass == question->qclass && // class, + IsLLQ(q) == IsLLQ(question) && // and long-lived status matches + (!q->AuthInfo || question->AuthInfo) && // to avoid deadlock, don't make public query dup of a private one + (q->AnonInfo == question->AnonInfo) && // Anonymous query not a dup of normal query + (q->SuppressQuery == question->SuppressQuery) && // Questions that are suppressed/not suppressed + (q->ValidationRequired == question->ValidationRequired) && // Questions that require DNSSEC validation + (q->ValidatingResponse == question->ValidatingResponse) && // Questions that are validating responses using DNSSEC + (q->DisallowPID == question->DisallowPID) && // Disallowing a PID should not affect a PID that is allowed + (q->BrowseThreshold == question->BrowseThreshold) && // browse thresholds must match + q->qnamehash == question->qnamehash && + (IsAWDLIncluded(q) == IsAWDLIncluded(question)) && // Inclusion of AWDL interface must match + SameDomainName(&q->qname, &question->qname)) // and name + return(q); + return(mDNSNULL); +} + +// This is called after a question is deleted, in case other identical questions were being suppressed as duplicates +mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const question) +{ + DNSQuestion *q; + DNSQuestion *first = mDNSNULL; + + // This is referring to some other question as duplicate. No other question can refer to this + // question as a duplicate. + if (question->DuplicateOf) + { + LogInfo("UpdateQuestionDuplicates: question %p %##s (%s) duplicate of %p %##s (%s)", + question, question->qname.c, DNSTypeName(question->qtype), + question->DuplicateOf, question->DuplicateOf->qname.c, DNSTypeName(question->DuplicateOf->qtype)); + return; + } + + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate + { + q->DuplicateOf = first; + if (!first) + { + first = q; + // If q used to be a duplicate, but now is not, + // then inherit the state from the question that's going away + q->LastQTime = question->LastQTime; + q->ThisQInterval = question->ThisQInterval; + q->ExpectUnicastResp = question->ExpectUnicastResp; + q->LastAnswerPktNum = question->LastAnswerPktNum; + q->RecentAnswerPkts = question->RecentAnswerPkts; + q->RequestUnicast = question->RequestUnicast; + q->LastQTxTime = question->LastQTxTime; + q->CNAMEReferrals = question->CNAMEReferrals; + q->nta = question->nta; + q->servAddr = question->servAddr; + q->servPort = question->servPort; + q->qDNSServer = question->qDNSServer; + q->validDNSServers = question->validDNSServers; + q->unansweredQueries = question->unansweredQueries; + q->noServerResponse = question->noServerResponse; + q->triedAllServersOnce = question->triedAllServersOnce; + + q->TargetQID = question->TargetQID; + if (q->LocalSocket) + { + mDNSPlatformUDPClose(q->LocalSocket); + } + + q->LocalSocket = question->LocalSocket; + + q->state = question->state; + // q->tcp = question->tcp; + q->ReqLease = question->ReqLease; + q->expire = question->expire; + q->ntries = question->ntries; + q->id = question->id; + + question->LocalSocket = mDNSNULL; + question->nta = mDNSNULL; // If we've got a GetZoneData in progress, transfer it to the newly active question + // question->tcp = mDNSNULL; + + if (q->LocalSocket) + debugf("UpdateQuestionDuplicates transferred LocalSocket pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (q->nta) + { + LogInfo("UpdateQuestionDuplicates transferred nta pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->nta->ZoneDataContext = q; + } + + // Need to work out how to safely transfer this state too -- appropriate context pointers need to be updated or the code will crash + if (question->tcp) LogInfo("UpdateQuestionDuplicates did not transfer tcp pointer"); + + if (question->state == LLQ_Established) + { + LogInfo("UpdateQuestionDuplicates transferred LLQ state for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + question->state = 0; // Must zero question->state, or mDNS_StopQuery_internal will clean up and cancel our LLQ from the server + } + + SetNextQueryTime(m,q); + } + } +} + +mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout) +{ + McastResolver **p = &m->McastResolvers; + McastResolver *tmp = mDNSNULL; + + if (!d) d = (const domainname *)""; + + LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout); + + mDNS_CheckLock(m); + + while (*p) // Check if we already have this {interface, domain} tuple registered + { + if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d)) + { + if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface); + (*p)->flags &= ~DNSServer_FlagDelete; + tmp = *p; + *p = tmp->next; + tmp->next = mDNSNULL; + } + else + p=&(*p)->next; + } + + if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer + else + { + // allocate, add to list + *p = mDNSPlatformMemAllocate(sizeof(**p)); + if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc"); + else + { + (*p)->interface = interface; + (*p)->flags = DNSServer_FlagNew; + (*p)->timeout = timeout; + AssignDomainName(&(*p)->domain, d); + (*p)->next = mDNSNULL; + } + } + return(*p); +} + +mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server) +{ + mDNSs32 ptime = 0; + if (server->penaltyTime != 0) + { + ptime = server->penaltyTime - m->timenow; + if (ptime < 0) + { + // This should always be a positive value between 0 and DNSSERVER_PENALTY_TIME + // If it does not get reset in ResetDNSServerPenalties for some reason, we do it + // here + LogMsg("PenaltyTimeForServer: PenaltyTime negative %d, (server penaltyTime %d, timenow %d) resetting the penalty", + ptime, server->penaltyTime, m->timenow); + server->penaltyTime = 0; + ptime = 0; + } + } + return ptime; +} + +//Checks to see whether the newname is a better match for the name, given the best one we have +//seen so far (given in bestcount). +//Returns -1 if the newname is not a better match +//Returns 0 if the newname is the same as the old match +//Returns 1 if the newname is a better match +mDNSlocal int BetterMatchForName(const domainname *name, int namecount, const domainname *newname, int newcount, + int bestcount) +{ + // If the name contains fewer labels than the new server's domain or the new name + // contains fewer labels than the current best, then it can't possibly be a better match + if (namecount < newcount || newcount < bestcount) return -1; + + // If there is no match, return -1 and the caller will skip this newname for + // selection + // + // If we find a match and the number of labels is the same as bestcount, then + // we return 0 so that the caller can do additional logic to pick one of + // the best based on some other factors e.g., penaltyTime + // + // If we find a match and the number of labels is more than bestcount, then we + // return 1 so that the caller can pick this over the old one. + // + // Note: newcount can either be equal or greater than bestcount beause of the + // check above. + + if (SameDomainName(SkipLeadingLabels(name, namecount - newcount), newname)) + return bestcount == newcount ? 0 : 1; + else + return -1; +} + +// Normally, we have McastResolvers for .local, in-addr.arpa and ip6.arpa. But there +// can be queries that can forced to multicast (ForceMCast) even though they don't end in these +// names. In that case, we give a default timeout of 5 seconds +#define DEFAULT_MCAST_TIMEOUT 5 +mDNSlocal mDNSu32 GetTimeoutForMcastQuestion(mDNS *m, DNSQuestion *question) +{ + McastResolver *curmatch = mDNSNULL; + int bestmatchlen = -1, namecount = CountLabels(&question->qname); + McastResolver *curr; + int bettermatch, currcount; + for (curr = m->McastResolvers; curr; curr = curr->next) + { + currcount = CountLabels(&curr->domain); + bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); + // Take the first best match. If there are multiple equally good matches (bettermatch = 0), we take + // the timeout value from the first one + if (bettermatch == 1) + { + curmatch = curr; + bestmatchlen = currcount; + } + } + LogInfo("GetTimeoutForMcastQuestion: question %##s curmatch %p, Timeout %d", question->qname.c, curmatch, + curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT); + return ( curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT); +} + +// Returns true if it is a Domain Enumeration Query +mDNSexport mDNSBool DomainEnumQuery(const domainname *qname) +{ + const mDNSu8 *mDNS_DEQLabels[] = { (const mDNSu8 *)"\001b", (const mDNSu8 *)"\002db", (const mDNSu8 *)"\002lb", + (const mDNSu8 *)"\001r", (const mDNSu8 *)"\002dr", (const mDNSu8 *)mDNSNULL, }; + const domainname *d = qname; + const mDNSu8 *label; + int i = 0; + + // We need at least 3 labels (DEQ prefix) + one more label to make a meaningful DE query + if (CountLabels(qname) < 4) { debugf("DomainEnumQuery: question %##s, not enough labels", qname->c); return mDNSfalse; } + + label = (const mDNSu8 *)d; + while (mDNS_DEQLabels[i] != (const mDNSu8 *)mDNSNULL) + { + if (SameDomainLabel(mDNS_DEQLabels[i], label)) {debugf("DomainEnumQuery: DEQ %##s, label1 match", qname->c); break;} + i++; + } + if (mDNS_DEQLabels[i] == (const mDNSu8 *)mDNSNULL) + { + debugf("DomainEnumQuery: Not a DEQ %##s, label1 mismatch", qname->c); + return mDNSfalse; + } + debugf("DomainEnumQuery: DEQ %##s, label1 match", qname->c); + + // CountLabels already verified the number of labels + d = (const domainname *)(d->c + 1 + d->c[0]); // Second Label + label = (const mDNSu8 *)d; + if (!SameDomainLabel(label, (const mDNSu8 *)"\007_dns-sd")) + { + debugf("DomainEnumQuery: Not a DEQ %##s, label2 mismatch", qname->c); + return(mDNSfalse); + } + debugf("DomainEnumQuery: DEQ %##s, label2 match", qname->c); + + d = (const domainname *)(d->c + 1 + d->c[0]); // Third Label + label = (const mDNSu8 *)d; + if (!SameDomainLabel(label, (const mDNSu8 *)"\004_udp")) + { + debugf("DomainEnumQuery: Not a DEQ %##s, label3 mismatch", qname->c); + return(mDNSfalse); + } + debugf("DomainEnumQuery: DEQ %##s, label3 match", qname->c); + + debugf("DomainEnumQuery: Question %##s is a Domain Enumeration query", qname->c); + + return mDNStrue; +} + +// Note: InterfaceID is the InterfaceID of the question +mDNSlocal mDNSBool DNSServerMatch(DNSServer *d, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID) +{ + // 1) Unscoped questions (NULL InterfaceID) should consider *only* unscoped DNSServers ( DNSServer + // with "scoped" set to kScopeNone) + // + // 2) Scoped questions (non-NULL InterfaceID) should consider *only* scoped DNSServers (DNSServer + // with "scoped" set to kScopeInterfaceId) and their InterfaceIDs should match. + // + // 3) Scoped questions (non-zero ServiceID) should consider *only* scoped DNSServers (DNSServer + // with "scoped" set to kScopeServiceID) and their ServiceIDs should match. + // + // The first condition in the "if" statement checks to see if both the question and the DNSServer are + // unscoped. The question is unscoped only if InterfaceID is zero and ServiceID is -1. + // + // If the first condition fails, following are the possible cases (the notes below are using + // InterfaceID for discussion and the same holds good for ServiceID): + // + // - DNSServer is not scoped, InterfaceID is not NULL - we should skip the current DNSServer entry + // as scoped questions should not pick non-scoped DNSServer entry (Refer to (2) above). + // + // - DNSServer is scoped, InterfaceID is NULL - we should skip the current DNSServer entry as + // unscoped question should not match scoped DNSServer (Refer to (1) above). The InterfaceID check + // would fail in this case. + // + // - DNSServer is scoped and InterfaceID is not NULL - the InterfaceID of the question and the DNSServer + // should match (Refer to (2) above). + // + // Note: mDNSInterface_Unicast is used only by .local unicast questions and are treated as unscoped. + // If a question is scoped both to InterfaceID and ServiceID, the question will be scoped to InterfaceID. + + if (((d->scoped == kScopeNone) && ((!InterfaceID && ServiceID == -1) || InterfaceID == mDNSInterface_Unicast)) || + ((d->scoped == kScopeInterfaceID) && d->interface == InterfaceID) || + ((d->scoped == kScopeServiceID) && d->serviceID == ServiceID)) + { + return mDNStrue; + } + return mDNSfalse; +} + +// Sets all the Valid DNS servers for a question +mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) +{ + int bestmatchlen = -1, namecount = CountLabels(&question->qname); + DNSServer *curr; + int bettermatch, currcount; + int index = 0; + mDNSu32 timeout = 0; + mDNSBool DEQuery; + + question->validDNSServers = zeroOpaque64; + DEQuery = DomainEnumQuery(&question->qname); + for (curr = m->DNSServers; curr; curr = curr->next) + { + debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped); + // skip servers that will soon be deleted + if (curr->flags & DNSServer_FlagDelete) + { + debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); + continue; + } + + // This happens normally when you unplug the interface where we reset the interfaceID to mDNSInterface_Any for all + // the DNS servers whose scope match the interfaceID. Few seconds later, we also receive the updated DNS configuration. + // But any questions that has mDNSInterface_Any scope that are started/restarted before we receive the update + // (e.g., CheckSuppressUnusableQuestions is called when interfaces are deregistered with the core) should not + // match the scoped entries by mistake. + // + // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout + + if (curr->scoped && curr->interface == mDNSInterface_Any) + { + debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); + continue; + } + + currcount = CountLabels(&curr->domain); + if ((!DEQuery || !curr->cellIntf) && DNSServerMatch(curr, question->InterfaceID, question->ServiceID)) + { + bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); + + // If we found a better match (bettermatch == 1) then clear all the bits + // corresponding to the old DNSServers that we have may set before and start fresh. + // If we find an equal match, then include that DNSServer also by setting the corresponding + // bit + if ((bettermatch == 1) || (bettermatch == 0)) + { + bestmatchlen = currcount; + if (bettermatch) + { + debugf("SetValidDNSServers: Resetting all the bits"); + question->validDNSServers = zeroOpaque64; + timeout = 0; + } + debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d," + " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout, + curr->interface); + timeout += curr->timeout; + if (DEQuery) + debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf); + bit_set_opaque64(question->validDNSServers, index); + } + } + index++; + } + question->noServerResponse = 0; + + debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x for question %p %##s (%s)", + question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype)); + // If there are no matching resolvers, then use the default timeout value. + // For ProxyQuestion, shorten the timeout so that dig does not timeout on us in case of no response. + return ((question->ProxyQuestion || question->ValidatingResponse) ? DEFAULT_UDNSSEC_TIMEOUT : timeout ? timeout : DEFAULT_UDNS_TIMEOUT); +} + +// Get the Best server that matches a name. If you find penalized servers, look for the one +// that will come out of the penalty box soon +mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID, mDNSOpaque64 validBits, + int *selected, mDNSBool nameMatch) +{ + DNSServer *curmatch = mDNSNULL; + int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0; + DNSServer *curr; + mDNSs32 bestPenaltyTime, currPenaltyTime; + int bettermatch, currcount; + int index = 0; + int currindex = -1; + + debugf("GetBestServer: ValidDNSServer bits 0x%x%x", validBits.l[1], validBits.l[0]); + bestPenaltyTime = DNSSERVER_PENALTY_TIME + 1; + for (curr = m->DNSServers; curr; curr = curr->next) + { + // skip servers that will soon be deleted + if (curr->flags & DNSServer_FlagDelete) + { + debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); + continue; + } + + // Check if this is a valid DNSServer + if (!bit_get_opaque64(validBits, index)) + { + debugf("GetBestServer: continuing for index %d", index); + index++; + continue; + } + + currcount = CountLabels(&curr->domain); + currPenaltyTime = PenaltyTimeForServer(m, curr); + + debugf("GetBestServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d", + &curr->addr, curr->domain.c, curr->penaltyTime, currPenaltyTime); + + // If there are multiple best servers for a given question, we will pick the first one + // if none of them are penalized. If some of them are penalized in that list, we pick + // the least penalized one. BetterMatchForName walks through all best matches and + // "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server + // in the list when there are no penalized servers and least one among them + // when there are some penalized servers. + + if (DNSServerMatch(curr, InterfaceID, ServiceID)) + { + + // If we know that all the names are already equally good matches, then skip calling BetterMatchForName. + // This happens when we initially walk all the DNS servers and set the validity bit on the question. + // Actually we just need PenaltyTime match, but for the sake of readability we just skip the expensive + // part and still do some redundant steps e.g., InterfaceID match + + if (nameMatch) + bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen); + else + bettermatch = 0; + + // If we found a better match (bettermatch == 1) then we don't need to + // compare penalty times. But if we found an equal match, then we compare + // the penalty times to pick a better match + + if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime)) + { + currindex = index; + curmatch = curr; + bestmatchlen = currcount; + bestPenaltyTime = currPenaltyTime; + } + } + index++; + } + if (selected) *selected = currindex; + return curmatch; +} + +// Look up a DNS Server, matching by name and InterfaceID +mDNSlocal DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID) +{ + DNSServer *curmatch = mDNSNULL; + char *ifname = mDNSNULL; // for logging purposes only + mDNSOpaque64 allValid; + + if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) + InterfaceID = mDNSNULL; + + if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); + + // By passing in all ones, we make sure that every DNS server is considered + allValid.l[0] = allValid.l[1] = 0xFFFFFFFF; + + curmatch = GetBestServer(m, name, InterfaceID, ServiceID, allValid, mDNSNULL, mDNStrue); + + if (curmatch != mDNSNULL) + LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr, + mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", + InterfaceID, name); + else + LogInfo("GetServerForName: no DNS server (Scope %s:%p) found for name %##s", ifname ? ifname : "None", InterfaceID, name); + + return(curmatch); +} + +// Look up a DNS Server for a question within its valid DNSServer bits +mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question) +{ + DNSServer *curmatch = mDNSNULL; + char *ifname = mDNSNULL; // for logging purposes only + mDNSInterfaceID InterfaceID = question->InterfaceID; + const domainname *name = &question->qname; + int currindex; + + if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) + InterfaceID = mDNSNULL; + + if (InterfaceID) + ifname = InterfaceNameForID(m, InterfaceID); + + if (!mDNSOpaque64IsZero(&question->validDNSServers)) + { + curmatch = GetBestServer(m, name, InterfaceID, question->ServiceID, question->validDNSServers, &currindex, mDNSfalse); + if (currindex != -1) + bit_clr_opaque64(question->validDNSServers, currindex); + } + + if (curmatch != mDNSNULL) + { + LogInfo("GetServerForQuestion: %p DNS server (%p) %#a:%d (Penalty Time Left %d) (Scope %s:%p:%d) found for name %##s (%s)", + question, curmatch, &curmatch->addr, mDNSVal16(curmatch->port), + (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", + InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype)); + } + else + { + LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p:%d) found for name %##s (%s)", + question, ifname ? ifname : "None", InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype)); + } + + return(curmatch); +} + #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)); + (mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort))) + +// Called in normal client context (lock not held) +mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n) +{ + DNSQuestion *q; + mDNS_Lock(m); + LogInfo("LLQNATCallback external address:port %.4a:%u, NAT result %d", &n->ExternalAddress, mDNSVal16(n->ExternalPort), n->Result); + n->clientContext = mDNSNULL; // we received at least one callback since starting this NAT-T + for (q = m->Questions; q; q=q->next) + if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) + startLLQHandshake(m, q); // If ExternalPort is zero, will do StartLLQPolling instead +#if APPLE_OSX_mDNSResponder + UpdateAutoTunnelDomainStatuses(m); +#endif + mDNS_Unlock(m); +} + +mDNSlocal mDNSBool IsPrivateDomain(mDNS *const m, DNSQuestion *q) +{ + DomainAuthInfo *AuthInfo; + // Skip Private domains as we have special addresses to get the hosts in the Private domain + AuthInfo = GetAuthInfoForName_internal(m, &q->qname); + if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel) + { + debugf("IsPrivateDomain: %##s true", q->qname.c); + return mDNStrue; + } + else + { + debugf("IsPrivateDomain: %##s false", q->qname.c); + return mDNSfalse; + } +} + +// This function takes the DNSServer as a separate argument because sometimes the +// caller has not yet assigned the DNSServer, but wants to evaluate the SuppressQuery +// status before switching to it. +mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNSServer *d) +{ + // Some callers don't check for the qtype + if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA) + { + LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + + // Private domains are exempted irrespective of what the DNSServer says + if (IsPrivateDomain(m, q)) + { + LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, Private Domain", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + + if (!d) + { + LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, as the DNS server is NULL", q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; + } + + // Check if the DNS Configuration allows A/AAAA queries to be sent + if ((q->qtype == kDNSType_A) && (d->req_A)) + { + LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows A queries", q->qname.c, + DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port)); + return mDNSfalse; + } + if ((q->qtype == kDNSType_AAAA) && (d->req_AAAA)) + { + LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows AAAA queries", q->qname.c, + DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port)); + return mDNSfalse; + } + + LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, since DNS Configuration does not allow (req_A is %s and req_AAAA is %s)", + q->qname.c, DNSTypeName(q->qtype), d->req_A ? "true" : "false", d->req_AAAA ? "true" : "false"); + + return mDNStrue; +} + +mDNSlocal mDNSBool ShouldSuppressDotLocalQuery(mDNS *const m, DNSQuestion *q) +{ + NetworkInterfaceInfo *intf; + AuthRecord *rr; + mDNSBool ret; + + // Check to see if there is at least one interface other than loopback and don't suppress + // .local questions if you find one. If we have at least one interface, it means that + // we can send unicast queries for the .local name and we don't want to suppress + // multicast in that case as upper layers don't know how to handle if we return a + // negative response for multicast followed by a positive response for unicast. + // + // Note: we used to check for multicast capable interfaces instead of just any interface + // present. That did not work in the case where we have a valid interface for unicast + // but not multicast capable e.g., cellular, as we ended up delivering a negative response + // first and the upper layer did not wait for the positive response that came later. + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->InterfaceActive && !intf->Loopback) + { + LogInfo("ShouldSuppressDotLocalQuery: Found interface %s, not suppressing", intf->ifname); + return mDNSfalse; + } + } + + // 1. If we find a LocalOnly or P2P record answering this question, then don't suppress it. + // Set m->CurrentQuestion as it is required by AnswerQuestionWithLORecord. + m->CurrentQuestion = q; + ret = AnswerQuestionWithLORecord(m, q, mDNStrue); + m->CurrentQuestion = mDNSNULL; + + if (ret) + { + LogInfo("ShouldSuppressDotLocalQuery: Found LocalOnly record for %##s (%s), not suppressing", q->qname.c, + DNSTypeName(q->qtype)); + return mDNSfalse; + } + + // 2. If we find a local AuthRecord answering this question, then don't suppress it. + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + LogInfo("ShouldSuppressDotLocalQuery: Found resource record %s for %##s (%s) not suppressing", ARDisplayString(m, rr), + q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + } + return mDNStrue; +} + +mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, DNSQuestion *q) +{ + if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA) + { + LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + + // We still want the ability to be able to listen to the local services and hence + // don't fail .local query if we have local records that can potentially answer + // the question. + if (q->InterfaceID != mDNSInterface_Unicast && IsLocalDomain(&q->qname)) + { + if (!ShouldSuppressDotLocalQuery(m, q)) + { + LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + else + { + LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, Local question", q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; + } + } + + return (ShouldSuppressUnicastQuery(m, q, q->qDNSServer)); +} + +mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q) +{ + CacheRecord *rr; + mDNSu32 slot; + CacheGroup *cg; + + slot = HashSlot(&q->qname); + cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + // Don't deliver RMV events for negative records + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + LogInfo("CacheRecordRmvEventsForCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d", + CRDisplayString(m, rr), q, q->qname.c, DNSTypeName(q->qtype), rr->CRActiveQuestion, q->CurrentAnswers); + continue; + } + + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + LogInfo("CacheRecordRmvEventsForCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s LocalAnswers %d", + q->qname.c, CRDisplayString(m, rr), q->LOAddressAnswers); + + q->CurrentAnswers--; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + + if (rr->CRActiveQuestion == q) + { + DNSQuestion *qptr; + // If this was the active question for this cache entry, it was the one that was + // responsible for keeping the cache entry fresh when the cache entry was reaching + // its expiry. We need to handover the responsibility to someone else. Otherwise, + // when the cache entry is about to expire, we won't find an active question + // (pointed by CRActiveQuestion) to refresh the cache. + for (qptr = m->Questions; qptr; qptr=qptr->next) + if (qptr != q && ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr)) + break; + + if (qptr) + LogInfo("CacheRecordRmvEventsForCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, " + "Original question CurrentAnswers %d, new question CurrentAnswers %d, SuppressUnusable %d, SuppressQuery %d", + qptr, CRDisplayString(m,rr), q->CurrentAnswers, qptr->CurrentAnswers, qptr->SuppressUnusable, qptr->SuppressQuery); + + rr->CRActiveQuestion = qptr; // Question used to be active; new value may or may not be null + if (!qptr) m->rrcache_active--; // If no longer active, decrement rrcache_active count + } + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } +} + +mDNSlocal mDNSBool IsQuestionNew(mDNS *const m, DNSQuestion *question) +{ + DNSQuestion *q; + for (q = m->NewQuestions; q; q = q->next) + if (q == question) return mDNStrue; + return mDNSfalse; +} + +mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q) +{ + AuthRecord *rr; + mDNSu32 slot; + AuthGroup *ag; + + if (m->CurrentQuestion) + LogMsg("LocalRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + if (IsQuestionNew(m, q)) + { + LogInfo("LocalRecordRmvEventsForQuestion: New Question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; + } + m->CurrentQuestion = q; + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + for (rr = ag->members; rr; rr=rr->next) + // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME + if (UniqueLocalOnlyRecord(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) + { + LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s", + ARDisplayString(m, rr)); + if (q->CurrentAnswers <= 0 || q->LOAddressAnswers <= 0) + { + LogMsg("LocalRecordRmvEventsForQuestion: ERROR!! CurrentAnswers or LOAddressAnswers is zero %p %##s" + " (%s) CurrentAnswers %d, LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), + q->CurrentAnswers, q->LOAddressAnswers); + continue; + } + AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_rmv); // MUST NOT dereference q again + if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; } + } + } + m->CurrentQuestion = mDNSNULL; + return mDNStrue; +} + +// Returns false if the question got deleted while delivering the RMV events +// The caller should handle the case +mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q) +{ + if (m->CurrentQuestion) + LogMsg("CacheRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + // If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events. + // If this question was answered using local auth records, then you can't deliver RMVs using cache + if (!IsQuestionNew(m, q) && !q->LOAddressAnswers) + { + m->CurrentQuestion = q; + CacheRecordRmvEventsForCurrentQuestion(m, q); + if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; } + m->CurrentQuestion = mDNSNULL; + } + else { LogInfo("CacheRecordRmvEventsForQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); } + return mDNStrue; +} + +mDNSlocal void SuppressStatusChanged(mDNS *const m, DNSQuestion *q, DNSQuestion **restart) +{ + // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero + // LOAddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before + // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers) + if (q->SuppressQuery) + { + q->SuppressQuery = mDNSfalse; + if (!CacheRecordRmvEventsForQuestion(m, q)) + { + LogInfo("SuppressStatusChanged: Question deleted while delivering RMV events from cache"); + return; + } + q->SuppressQuery = mDNStrue; + } + + // SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts) + // and SuppressQuery status does not mean anything for these questions. As we are going to stop the + // question below, we need to deliver the RMV events so that the ADDs that will be delivered during + // the restart will not be a duplicate ADD + if (!LocalRecordRmvEventsForQuestion(m, q)) + { + LogInfo("SuppressStatusChanged: Question deleted while delivering RMV events from Local AuthRecords"); + return; + } + + // There are two cases here. + // + // 1. Previously it was suppressed and now it is not suppressed, restart the question so + // that it will start as a new question. Note that we can't just call ActivateUnicastQuery + // because when we get the response, if we had entries in the cache already, it will not answer + // this question if the cache entry did not change. Hence, we need to restart + // the query so that it can be answered from the cache. + // + // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions + // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question + // is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false). + // A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed + // (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an + // immediate response and not want to be blocked behind a question that is querying DNS servers. When + // the question is not suppressed, we don't want two active questions sending packets on the wire. + // This affects both efficiency and also the current design where there is only one active question + // pointed to from a cache entry. + // + // We restart queries in a two step process by first calling stop and build a temporary list which we + // will restart at the end. The main reason for the two step process is to handle duplicate questions. + // If there are duplicate questions, calling stop inherits the values from another question on the list (which + // will soon become the real question) including q->ThisQInterval which might be zero if it was + // suppressed before. At the end when we have restarted all questions, none of them is active as each + // inherits from one another and we need to reactivate one of the questions here which is a little hacky. + // + // It is much cleaner and less error prone to build a list of questions and restart at the end. + + LogInfo("SuppressStatusChanged: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StopQuery_internal(m, q); + q->next = *restart; + *restart = q; +} + +// The caller should hold the lock +mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) +{ + DNSQuestion *q; + DNSQuestion *restart = mDNSNULL; + + // We look through all questions including new questions. During network change events, + // we potentially restart questions here in this function that ends up as new questions, + // which may be suppressed at this instance. Before it is handled we get another network + // event that changes the status e.g., address becomes available. If we did not process + // new questions, we would never change its SuppressQuery status. + // + // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the + // application callback can potentially stop the current question (detected by CurrentQuestion) or + // *any* other question which could be the next one that we may process here. RestartQuestion + // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal + // if the "next" question is stopped while the CurrentQuestion is stopped + if (m->RestartQuestion) + LogMsg("CheckSuppressUnusableQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)", + m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype)); + m->RestartQuestion = m->Questions; + while (m->RestartQuestion) + { + q = m->RestartQuestion; + m->RestartQuestion = q->next; + if (q->SuppressUnusable) + { + mDNSBool old = q->SuppressQuery; + q->SuppressQuery = ShouldSuppressQuery(m, q); + if (q->SuppressQuery != old) + { + // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before + // followed by a negative cache response. Temporarily turn off suppression so that + // AnswerCurrentQuestionWithResourceRecord can answer the question + SuppressStatusChanged(m, q, &restart); + } + } + } + while (restart) + { + q = restart; + restart = restart->next; + q->next = mDNSNULL; + LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StartQuery_internal(m, q); + } +} + +mDNSlocal void RestartUnicastQuestions(mDNS *const m) +{ + DNSQuestion *q; + DNSQuestion *restart = mDNSNULL; + + if (m->RestartQuestion) + LogMsg("RestartUnicastQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)", + m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype)); + m->RestartQuestion = m->Questions; + while (m->RestartQuestion) + { + q = m->RestartQuestion; + m->RestartQuestion = q->next; + if (q->Restart) + { + if (mDNSOpaque16IsZero(q->TargetQID)) + LogMsg("RestartUnicastQuestions: ERROR!! Restart set for multicast question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + q->Restart = 0; + SuppressStatusChanged(m, q, &restart); + } + } + while (restart) + { + q = restart; + restart = restart->next; + q->next = mDNSNULL; + LogInfo("RestartUnicastQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StartQuery_internal(m, q); + } +} + + +// ValidateParameters() is called by mDNS_StartQuery_internal() to check the client parameters of +// DNS Question that are already set by the client before calling mDNS_StartQuery() +mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question) +{ + + if (question->Target.type && !ValidQuestionTarget(question)) + { + LogMsg("ValidateParameters: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)", + question->Target.type, mDNSVal16(question->TargetPort), question->qname.c); + question->Target.type = mDNSAddrType_None; + } + + // If no question->Target specified, clear TargetPort + if (!question->Target.type) + question->TargetPort = zeroIPPort; + + if (!ValidateDomainName(&question->qname)) + { + LogMsg("ValidateParameters: Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return(mStatus_Invalid); + } + + // If this question is referencing a specific interface, verify it exists + if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P) + { + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); + if (!intf) + LogInfo("ValidateParameters: Note: InterfaceID %d for question %##s (%s) not currently found in active interface list", + (uint32_t)question->InterfaceID, question->qname.c, DNSTypeName(question->qtype)); + } + + return(mStatus_NoError); +} + +// InitDNSConfig() is called by InitCommonState() to initialize the DNS configuration of the Question. +// These are a subset of the internal uDNS fields. Must be done before ShouldSuppressQuery() & mDNS_PurgeForQuestion() +mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) +{ + // First reset all DNS Configuration + question->qDNSServer = mDNSNULL; + question->validDNSServers = zeroOpaque64; + question->triedAllServersOnce = 0; + question->noServerResponse = 0; + question->StopTime = 0; + + // Need not initialize the DNS Configuration for Local Only OR P2P Questions + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + return; + // Proceed to initialize DNS Configuration (some are set in SetValidDNSServers()) + if (!mDNSOpaque16IsZero(question->TargetQID)) + { + mDNSu32 timeout = SetValidDNSServers(m, question); + mDNSIPPort zp = zeroIPPort; + // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have + // a networking change/search domain change that calls this function again we keep + // reinitializing the timeout value which means it may never timeout. If this becomes + // a common case in the future, we can easily fix this by adding extra state that + // indicates that we have already set the StopTime. + // + // Note that we set the timeout for all questions. If this turns out to be a duplicate, + // it gets a full timeout value even if the original question times out earlier. + if (question->TimeoutQuestion) + { + question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); + LogInfo("InitDNSConfig: Setting StopTime on question %p %##s (%s)", question, question->qname.c, DNSTypeName(question->qtype)); + } + + question->qDNSServer = GetServerForQuestion(m, question); + LogInfo("InitDNSConfig: question %p %##s (%s) Timeout %d, DNS Server %#a:%d", + question, question->qname.c, DNSTypeName(question->qtype), timeout, + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zp)); + } + else + { + if (question->TimeoutQuestion) + question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond); + } + // Set StopTime here since it is a part of DNS Configuration + if (question->StopTime) + SetNextQueryStopTime(m, question); + // SetNextQueryTime() need not be initialized for LocalOnly OR P2P Questions since those questions + // will never be transmitted on the wire. Hence we call SetNextQueryTime() here. + SetNextQueryTime(m,question); +} + +// InitCommonState() is called by mDNS_StartQuery_internal() to initialize the common(uDNS/mDNS) internal +// state fields of the DNS Question. These are independent of the Client layer. +mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) +{ + mDNSBool purge; + int i; + + // Note: In the case where we already have the answer to this question in our cache, that may be all the client + // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would + // be a waste. For that reason, we schedule our first query to go out in half a second (InitialQuestionInterval). + // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate + // that to go out immediately. + question->next = mDNSNULL; + // ThisQInterval should be initialized before any memory allocations occur. If malloc + // debugging is turned on within mDNSResponder (see mDNSDebug.h for details) it validates + // the question list to check if ThisQInterval is negative which means the question has been + // stopped and can't be on the list. The question is already on the list and ThisQInterval + // can be negative if the caller just stopped it and starting it again. Hence, it always has to + // be initialized. CheckForSoonToExpireRecords below prints the cache records when logging is + // turned ON which can allocate memory e.g., base64 encoding, in the case of DNSSEC. + question->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + question->qnamehash = DomainNameHashValue(&question->qname); + question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname), &purge); + question->LastQTime = m->timenow; + question->ExpectUnicastResp = 0; + question->LastAnswerPktNum = m->PktNum; + question->RecentAnswerPkts = 0; + question->CurrentAnswers = 0; + +#if APPLE_OSX_mDNSResponder + +// Initial browse threshold used by Finder. +#define mDNSFinderBrowseThreshold 20 + + // Set the threshold at which we move to a passive browse state, + // not actively sending queries. + if (question->flags & kDNSServiceFlagsThresholdOne) + question->BrowseThreshold = 1; + else if (question->flags & kDNSServiceFlagsThresholdFinder) + question->BrowseThreshold = mDNSFinderBrowseThreshold; + else + question->BrowseThreshold = 0; + +#else // APPLE_OSX_mDNSResponder + question->BrowseThreshold = 0; +#endif // APPLE_OSX_mDNSResponder + question->CachedAnswerNeedsUpdate = mDNSfalse; + + question->LargeAnswers = 0; + question->UniqueAnswers = 0; + question->LOAddressAnswers = 0; + question->FlappingInterface1 = mDNSNULL; + question->FlappingInterface2 = mDNSNULL; - if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated - return(mStatus_NoCache); + // if kDNSServiceFlagsServiceIndex flag is SET by the client, then do NOT call mDNSPlatformGetServiceID() + // since we would already have the question->ServiceID in that case. + if (!(question->flags & kDNSServiceFlagsServiceIndex)) + question->ServiceID = mDNSPlatformGetServiceID(m, question); 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); + LogInfo("InitCommonState: Query for %##s (%s), PID[%d], ServiceID %d is already set by client", question->qname.c, + DNSTypeName(question->qtype), question->pid, question->ServiceID); - 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); - } + InitDNSConfig(m, question); + + question->AuthInfo = GetAuthInfoForQuestion(m, question); + question->SuppressQuery = 0; + if (question->SuppressUnusable) + question->SuppressQuery = ShouldSuppressQuery(m, question); + + // If ServiceID is 0 or the policy disallows making DNS requests, + // set DisallowPID + question->DisallowPID = (question->ServiceID == 0 || (mDNSPlatformAllowPID(m, question) == 0)); + if (question->DisallowPID) + LogInfo("InitCommonState: Query suppressed for %##s (%s), PID %d/ServiceID %d not allowed", question->qname.c, + DNSTypeName(question->qtype), question->pid, question->ServiceID); + + question->NextInDQList = mDNSNULL; + question->SendQNow = mDNSNULL; + question->SendOnAll = mDNSfalse; + +#if mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; +#else // mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = SET_QU_IN_FIRST_QUERY; +#endif // mDNS_REQUEST_UNICAST_RESPONSE + +#if APPLE_OSX_mDNSResponder + // Request unicast response for first 4 queries to increase + // reliability in an environment with high multicast packet loss. + // Must set to one more than the number of unicast queries you want, since SendQueries() + // decrements it before calling BuildQuestion() which acts on it. + if (question->flags & kDNSServiceFlagsUnicastResponse) + { + question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; + LogInfo("InitCommonState: setting RequestUnicast = %d for %##s (%s)", question->RequestUnicast, question->qname.c, + DNSTypeName(question->qtype)); + } + else if (question->flags & kDNSServiceFlagsThresholdFinder) + { + // always send one request with QU bit set when kDNSServiceFlagsThresholdFinder is set +#if mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; +#else // mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = SET_QU_IN_FIRST_QUERY; +#endif // mDNS_REQUEST_UNICAST_RESPONSE + + LogInfo("InitCommonState: kDNSServiceFlagsThresholdFinder set, setting RequestUnicast = %d for %##s (%s)", + question->RequestUnicast, question->qname.c, DNSTypeName(question->qtype)); + } +#endif // APPLE_OSX_mDNSResponder + + question->LastQTxTime = m->timenow; + question->CNAMEReferrals = 0; + + question->WakeOnResolveCount = 0; + if (question->WakeOnResolve) + { + question->WakeOnResolveCount = InitialWakeOnResolveCount; + purge = mDNStrue; + } + + for (i=0; i<DupSuppressInfoSize; i++) + question->DupSuppress[i].InterfaceID = mDNSNULL; + + question->Restart = 0; + + debugf("InitCommonState: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow, + NextQSendTime(question) - m->timenow, + question->DelayAnswering ? question->DelayAnswering - m->timenow : 0, + question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf); + + if (question->DelayAnswering) + LogInfo("InitCommonState: Delaying answering for %d ticks while cache stabilizes for %##s (%s)", + question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype)); + + return(purge); +} + +// Excludes the DNS Config fields which are already handled by InitDNSConfig() +mDNSlocal void InitWABState(DNSQuestion *const question) +{ + // We'll create our question->LocalSocket on demand, if needed. + // We won't need one for duplicate questions, or from questions answered immediately out of the cache. + // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single + // NAT mapping for receiving inbound add/remove events. + question->LocalSocket = mDNSNULL; + question->unansweredQueries = 0; + question->nta = mDNSNULL; + question->servAddr = zeroAddr; + question->servPort = zeroIPPort; + question->tcp = mDNSNULL; + question->NoAnswer = NoAnswer_Normal; +} + +mDNSlocal void InitLLQNATState(mDNS *const m) +{ + // If we don't have our NAT mapping active, start it now + if (!m->LLQNAT.clientCallback) + { + m->LLQNAT.Protocol = NATOp_MapUDP; + m->LLQNAT.IntPort = m->UnicastPort4; + m->LLQNAT.RequestedPort = m->UnicastPort4; + m->LLQNAT.clientCallback = LLQNATCallback; + m->LLQNAT.clientContext = (void*)1; // Means LLQ NAT Traversal just started + mDNS_StartNATOperation_internal(m, &m->LLQNAT); + } +} + +mDNSlocal void InitLLQState(DNSQuestion *const question) +{ + question->state = LLQ_InitialRequest; + question->ReqLease = 0; + question->expire = 0; + question->ntries = 0; + question->id = zeroOpaque64; +} + +// InitDNSSECProxyState() is called by mDNS_StartQuery_internal() to initialize +// DNSSEC & DNS Proxy fields of the DNS Question. +mDNSlocal void InitDNSSECProxyState(mDNS *const m, DNSQuestion *const question) +{ + (void) m; + + // DNS server selection affects DNSSEC. Turn off validation if req_DO is not set + // or the request is going over cellular interface. + // + // Note: This needs to be done here before we call FindDuplicateQuestion as it looks + // at ValidationRequired setting also. + if (question->qDNSServer) + { + if (question->qDNSServer->cellIntf) + { + LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); going over cell", question->qname.c, DNSTypeName(question->qtype)); + question->ValidationRequired = mDNSfalse; + } + if (DNSSECOptionalQuestion(question) && !(question->qDNSServer->req_DO)) + { + LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); req_DO false", + question->qname.c, DNSTypeName(question->qtype)); + question->ValidationRequired = DNSSEC_VALIDATION_NONE; + } + } + question->ValidationState = (question->ValidationRequired ? DNSSECValRequired : DNSSECValNotRequired); + question->ValidationStatus = 0; + question->responseFlags = zeroID; +} + +// Once the question is completely initialized including the duplicate logic, this function +// is called to finalize the unicast question which requires flushing the cache if needed, +// activating the query etc. +mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question, mDNSBool purge) +{ + // Ensure DNS related info of duplicate question is same as the orig question + if (question->DuplicateOf) + { + mDNSIPPort zp = zeroIPPort; + question->validDNSServers = question->DuplicateOf->validDNSServers; + question->qDNSServer = question->DuplicateOf->qDNSServer; + LogInfo("FinalizeUnicastQuestion: Duplicate question %p (%p) %##s (%s), DNS Server %#a:%d", + question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zp)); + } + + ActivateUnicastQuery(m, question, mDNSfalse); + + // If purge was set above, flush the cache. Need to do this after we set the + // DNS server on the question + if (purge) + { + question->DelayAnswering = 0; + mDNS_PurgeForQuestion(m, question); + } + else if (!question->DuplicateOf && DNSSECQuestion(question)) + { + // For DNSSEC questions, we need to have the RRSIGs also for verification. + CheckForDNSSECRecords(m, question); + } + if (question->LongLived) + { + // Unlike other initializations, InitLLQNATState should be done after + // we determine that it is a unicast question. LongLived is set for + // both multicast and unicast browse questions but we should initialize + // the LLQ NAT state only for unicast. Otherwise we will unnecessarily + // start the NAT traversal that is not needed. + InitLLQNATState(m); +#if APPLE_OSX_mDNSResponder + UpdateAutoTunnelDomainStatuses(m); +#endif + } +} + +mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) +{ + DNSQuestion **q; + mStatus vStatus; + mDNSBool purge; + mDNSOpaque16 zqid = zeroID; + + // First check for cache space (can't do queries if there is no cache space allocated) + if (m->rrcache_size == 0) + return(mStatus_NoCache); + + vStatus = ValidateParameters(m, question); + if (vStatus) + return(vStatus); + + question->TargetQID = +#ifndef UNICAST_DISABLED + (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : +#endif // UNICAST_DISABLED + zqid; + debugf("mDNS_StartQuery_internal: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + + // Note: It important that new questions are appended at the *end* of the list, not prepended at the start + q = &m->Questions; + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + q = &m->LocalOnlyQuestions; + while (*q && *q != question) + q=&(*q)->next; + + if (*q) + { + LogMsg("mDNS_StartQuery_internal: Error! Tried to add a question %##s (%s) %p that's already in the active list", + question->qname.c, DNSTypeName(question->qtype), question); + return(mStatus_AlreadyRegistered); + } + *q = question; + + + // Intialize the question. The only ordering constraint we have today is that + // InitDNSSECProxyState should be called after the DNS server is selected (in + // InitCommonState -> InitDNSConfig) as DNS server selection affects DNSSEC + // validation. + + purge = InitCommonState(m, question); + InitWABState(question); + InitLLQState(question); + InitDNSSECProxyState(m, question); + + // FindDuplicateQuestion should be called last after all the intialization + // as the duplicate logic could be potentially based on any field in the + // question. + question->DuplicateOf = FindDuplicateQuestion(m, question); + if (question->DuplicateOf) + question->AuthInfo = question->DuplicateOf->AuthInfo; + + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + { + if (!m->NewLocalOnlyQuestions) + m->NewLocalOnlyQuestions = question; + } + else + { + if (!m->NewQuestions) + m->NewQuestions = question; + + // If the question's id is non-zero, then it's Wide Area + // MUST NOT do this Wide Area setup until near the end of + // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA, + // NS, etc.) and if we haven't finished setting up our own question and setting + // m->NewQuestions if necessary then we could end up recursively re-entering + // this routine with the question list data structures in an inconsistent state. + if (!mDNSOpaque16IsZero(question->TargetQID)) + { + FinalizeUnicastQuestion(m, question, purge); + } + else + { + if (purge) + { + LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c); + mDNS_PurgeForQuestion(m, question); + } + } + } + + return(mStatus_NoError); +} + +// CancelGetZoneData is an internal routine (i.e. must be called with the lock already held) +mDNSexport void CancelGetZoneData(mDNS *const m, ZoneData *nta) +{ + debugf("CancelGetZoneData %##s (%s)", nta->question.qname.c, DNSTypeName(nta->question.qtype)); + // This function may be called anytime to free the zone information.The question may or may not have stopped. + // If it was already stopped, mDNS_StopQuery_internal would have set q->ThisQInterval to -1 and should not + // call it again + if (nta->question.ThisQInterval != -1) + { + mDNS_StopQuery_internal(m, &nta->question); + if (nta->question.ThisQInterval != -1) + LogMsg("CancelGetZoneData: Question %##s (%s) ThisQInterval %d not -1", nta->question.qname.c, DNSTypeName(nta->question.qtype), nta->question.ThisQInterval); + } + mDNSPlatformMemFree(nta); +} + +mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question) +{ + const mDNSu32 slot = HashSlot(&question->qname); + CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); + CacheRecord *rr; + DNSQuestion **qp = &m->Questions; + + //LogInfo("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) qp = &m->LocalOnlyQuestions; + while (*qp && *qp != question) qp=&(*qp)->next; + if (*qp) *qp = (*qp)->next; + else + { +#if !ForceAlerts + if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active +#endif + LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", + question->qname.c, DNSTypeName(question->qtype)); +#if ForceAlerts + *(long*)0 = 0; +#endif + return(mStatus_BadReferenceErr); + } + + // Take care to cut question from list *before* calling UpdateQuestionDuplicates + UpdateQuestionDuplicates(m, question); + // But don't trash ThisQInterval until afterwards. + question->ThisQInterval = -1; + + // If there are any cache records referencing this as their active question, then see if there is any + // other question that is also referencing them, else their CRActiveQuestion needs to get set to NULL. + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + if (rr->CRActiveQuestion == question) + { + DNSQuestion *q; + // Checking for ActiveQuestion filters questions that are suppressed also + // as suppressed questions are not active + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + break; + if (q) + debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s, Original question CurrentAnswers %d, new question " + "CurrentAnswers %d, SuppressQuery %d", q, CRDisplayString(m,rr), question->CurrentAnswers, q->CurrentAnswers, q->SuppressQuery); + rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null + if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count + } + } + + // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv() is about to look at, + // bump its pointer forward one question. + if (m->CurrentQuestion == question) + { + debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->CurrentQuestion = question->next; + } + + if (m->NewQuestions == question) + { + debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->NewQuestions = question->next; + } + + if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next; + + if (m->RestartQuestion == question) + { + LogMsg("mDNS_StopQuery_internal: Just deleted the current restart question: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->RestartQuestion = question->next; + } + + if (m->ValidationQuestion == question) + { + LogInfo("mDNS_StopQuery_internal: Just deleted the current Validation question: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->ValidationQuestion = question->next; + } + + // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions + question->next = mDNSNULL; + + // LogMsg("mDNS_StopQuery_internal: Question %##s (%s) removed", question->qname.c, DNSTypeName(question->qtype)); + + // And finally, cancel any associated GetZoneData operation that's still running. + // Must not do this until last, because there's a good chance the GetZoneData question is the next in the list, + // so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already + // invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary + // *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query. + if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } + if (question->LocalSocket) { mDNSPlatformUDPClose(question->LocalSocket); question->LocalSocket = mDNSNULL; } + if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived) + { + // Scan our list to see if any more wide-area LLQs remain. If not, stop our NAT Traversal. + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break; + if (!q) + { + if (!m->LLQNAT.clientCallback) // Should never happen, but just in case... + { + LogMsg("mDNS_StopQuery ERROR LLQNAT.clientCallback NULL"); + } + else + { + LogInfo("Stopping LLQNAT"); + mDNS_StopNATOperation_internal(m, &m->LLQNAT); + m->LLQNAT.clientCallback = mDNSNULL; // Means LLQ NAT Traversal not running + } + } + + // If necessary, tell server it can delete this LLQ state + if (question->state == LLQ_Established) + { + question->ReqLease = 0; + sendLLQRefresh(m, question); + // If we need need to make a TCP connection to cancel the LLQ, that's going to take a little while. + // We clear the tcp->question backpointer so that when the TCP connection completes, it doesn't + // crash trying to access our cancelled question, but we don't cancel the TCP operation itself -- + // we let that run out its natural course and complete asynchronously. + if (question->tcp) + { + question->tcp->question = mDNSNULL; + question->tcp = mDNSNULL; + } + } +#if APPLE_OSX_mDNSResponder + UpdateAutoTunnelDomainStatuses(m); +#endif + } + // wait until we send the refresh above which needs the nta + if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } + + if (question->ValidationRequired && question->DNSSECAuthInfo) + { + LogInfo("mDNS_StopQuery_internal: freeing DNSSECAuthInfo %##s", question->qname.c); + question->DAIFreeCallback(m, question->DNSSECAuthInfo); + question->DNSSECAuthInfo = mDNSNULL; + } + if (question->AnonInfo) + { + FreeAnonInfo(question->AnonInfo); + question->AnonInfo = 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); - } +{ + 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); - } +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StopQuery_internal(m, question); + mDNS_Unlock(m); + return(status); +} + +// Note that mDNS_StopQueryWithRemoves() does not currently implement the full generality of the other APIs +// Specifically, question callbacks invoked as a result of this call cannot themselves make API calls. +// We invoke the callback without using mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback +// specifically to catch and report if the client callback does try to make API calls +mDNSexport mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const question) +{ + mStatus status; + DNSQuestion *qq; + mDNS_Lock(m); + + // Check if question is new -- don't want to give remove events for a question we haven't even answered yet + for (qq = m->NewQuestions; qq; qq=qq->next) if (qq == question) break; + + status = mDNS_StopQuery_internal(m, question); + if (status == mStatus_NoError && !qq) + { + const CacheRecord *rr; + const mDNSu32 slot = HashSlot(&question->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); + LogInfo("Generating terminal removes for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (rr->resrec.RecordType != kDNSRecordTypePacketNegative && SameNameRecordAnswersQuestion(&rr->resrec, question)) + { + // Don't use mDNS_DropLockBeforeCallback() here, since we don't allow API calls + if (question->QuestionCallback) + question->QuestionCallback(m, question, &rr->resrec, QC_rmv); + } + } + mDNS_Unlock(m); + return(status); +} + +mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const cr) +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0); + mDNS_Unlock(m); + return(status); +} mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr) - { - mStatus status = mStatus_BadReferenceErr; - CacheRecord *cr; - mDNS_Lock(m); - cr = FindIdenticalRecordInCache(m, rr); - if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); - mDNS_Unlock(m); - return(status); - } +{ + mStatus status = mStatus_BadReferenceErr; + CacheRecord *cr; + mDNS_Lock(m); + cr = FindIdenticalRecordInCache(m, rr); + debugf("mDNS_ReconfirmByValue: %p %s", cr, RRDisplayString(m, rr)); + if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0); + mDNS_Unlock(m); + return(status); +} + +mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const question, + const domainname *const srv, const domainname *const domain, + const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags, + mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, + mDNSQuestionCallback *Callback, void *Context) +{ + question->InterfaceID = InterfaceID; + question->flags = flags; + question->Target = zeroAddr; + question->qtype = kDNSType_PTR; + question->qclass = kDNSClass_IN; + question->LongLived = mDNStrue; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = ForceMCast; + question->ReturnIntermed = mDNSfalse; + question->SuppressUnusable = mDNSfalse; + question->DenyOnCellInterface = mDNSfalse; + question->DenyOnExpInterface = mDNSfalse; + question->SearchListIndex = 0; + question->AppendSearchDomains = 0; + question->RetryWithSearchDomains = mDNSfalse; + question->TimeoutQuestion = 0; + question->WakeOnResolve = 0; + question->UseBackgroundTrafficClass = useBackgroundTrafficClass; + question->ValidationRequired = 0; + question->ValidatingResponse = 0; + question->ProxyQuestion = 0; + question->qnameOrig = mDNSNULL; + question->AnonInfo = mDNSNULL; + question->QuestionCallback = Callback; + question->QuestionContext = Context; + + if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) + return(mStatus_BadParamErr); + + if (anondata) + { + question->AnonInfo = AllocateAnonInfo(&question->qname, anondata, mDNSPlatformStrLen(anondata), mDNSNULL); + if (!question->AnonInfo) + return(mStatus_BadParamErr); + } + + return(mDNS_StartQuery_internal(m, question)); +} mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, - const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context) - { - 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 - } + const domainname *const srv, const domainname *const domain, + const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags, + mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, + mDNSQuestionCallback *Callback, void *Context) +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StartBrowse_internal(m, question, srv, domain, anondata, InterfaceID, flags, ForceMCast, useBackgroundTrafficClass, Callback, Context); + mDNS_Unlock(m); + return(status); +} mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m) - { - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); - return(mDNSfalse); - } - -mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, 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); - } - } +{ + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); + return(mDNSfalse); +} + +mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + mDNSBool PortChanged = !mDNSSameIPPort(query->info->port, answer->rdata->u.srv.port); + if (!AddRecord) return; + if (answer->rrtype != kDNSType_SRV) return; + + query->info->port = answer->rdata->u.srv.port; + + // If this is our first answer, then set the GotSRV flag and start the address query + if (!query->GotSRV) + { + query->GotSRV = mDNStrue; + query->qAv4.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); + query->qAv6.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); + mDNS_StartQuery(m, &query->qAv4); + // Only do the AAAA query if this machine actually has IPv6 active + if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); + } + // If this is not our first answer, only re-issue the address query if the target host name has changed + else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || + !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) + { + mDNS_StopQuery(m, &query->qAv4); + if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6); + if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) + { + // If we get here, it means: + // 1. This is not our first SRV answer + // 2. The interface ID is different, but the target host and port are the same + // This implies that we're seeing the exact same SRV record on more than one interface, so we should + // make our address queries at least as broad as the original SRV query so that we catch all the answers. + query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface + query->qAv6.InterfaceID = query->qSRV.InterfaceID; + } + else + { + query->qAv4.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); + query->qAv6.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); + } + debugf("FoundServiceInfoSRV: Restarting address queries for %##s (%s)", query->qAv4.qname.c, DNSTypeName(query->qAv4.qtype)); + mDNS_StartQuery(m, &query->qAv4); + // Only do the AAAA query if this machine actually has IPv6 active + if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); + } + else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) + { + if (++query->Answers >= 100) + debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", + query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, + mDNSVal16(answer->rdata->u.srv.port)); + query->ServiceInfoQueryCallback(m, query); + } + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. +} + +mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + if (!AddRecord) return; + if (answer->rrtype != kDNSType_TXT) return; + if (answer->rdlength > sizeof(query->info->TXTinfo)) return; + + query->GotTXT = mDNStrue; + query->info->TXTlen = answer->rdlength; + query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero + mDNSPlatformMemCopy(query->info->TXTinfo, answer->rdata->u.txt.c, answer->rdlength); + + verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); + + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. + if (query->ServiceInfoQueryCallback && query->GotADD) + { + if (++query->Answers >= 100) + debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", + query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); + query->ServiceInfoQueryCallback(m, query); + } +} + +mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + //LogInfo("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer)); + if (!AddRecord) return; + + if (answer->rrtype == kDNSType_A) + { + query->info->ip.type = mDNSAddrType_IPv4; + query->info->ip.ip.v4 = answer->rdata->u.ipv4; + } + else if (answer->rrtype == kDNSType_AAAA) + { + query->info->ip.type = mDNSAddrType_IPv6; + query->info->ip.ip.v6 = answer->rdata->u.ipv6; + } + else + { + debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype)); + return; + } + + query->GotADD = mDNStrue; + query->info->InterfaceID = answer->InterfaceID; + + verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); + + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. + if (query->ServiceInfoQueryCallback && query->GotTXT) + { + if (++query->Answers >= 100) + debugf(answer->rrtype == kDNSType_A ? + "**** WARNING **** have given %lu answers for %##s (A) %.4a" : + "**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", + query->Answers, query->qSRV.qname.c, &answer->rdata->u.data); + query->ServiceInfoQueryCallback(m, query); + } +} // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure // If the query is not interface-specific, then InterfaceID may be zero // Each time the Callback is invoked, the remainder of the fields will have been filled in // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, - ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) - { - mStatus status; - mDNS_Lock(m); - - query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qSRV.InterfaceID = info->InterfaceID; - query->qSRV.Target = zeroAddr; - AssignDomainName(&query->qSRV.qname, &info->name); - query->qSRV.qtype = kDNSType_SRV; - query->qSRV.qclass = kDNSClass_IN; - query->qSRV.LongLived = mDNSfalse; - query->qSRV.ExpectUnique = mDNStrue; - query->qSRV.ForceMCast = mDNSfalse; - query->qSRV.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; + 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.flags = 0; + query->qSRV.Target = zeroAddr; + AssignDomainName(&query->qSRV.qname, &info->name); + query->qSRV.qtype = kDNSType_SRV; + query->qSRV.qclass = kDNSClass_IN; + query->qSRV.LongLived = mDNSfalse; + query->qSRV.ExpectUnique = mDNStrue; + query->qSRV.ForceMCast = mDNSfalse; + query->qSRV.ReturnIntermed = mDNSfalse; + query->qSRV.SuppressUnusable = mDNSfalse; + query->qSRV.DenyOnCellInterface = mDNSfalse; + query->qSRV.DenyOnExpInterface = mDNSfalse; + query->qSRV.SearchListIndex = 0; + query->qSRV.AppendSearchDomains = 0; + query->qSRV.RetryWithSearchDomains = mDNSfalse; + query->qSRV.TimeoutQuestion = 0; + query->qSRV.WakeOnResolve = 0; + query->qSRV.UseBackgroundTrafficClass = mDNSfalse; + query->qSRV.ValidationRequired = 0; + query->qSRV.ValidatingResponse = 0; + query->qSRV.ProxyQuestion = 0; + query->qSRV.qnameOrig = mDNSNULL; + query->qSRV.AnonInfo = mDNSNULL; + query->qSRV.QuestionCallback = FoundServiceInfoSRV; + query->qSRV.QuestionContext = query; + + query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qTXT.InterfaceID = info->InterfaceID; + query->qTXT.flags = 0; + query->qTXT.Target = zeroAddr; + AssignDomainName(&query->qTXT.qname, &info->name); + query->qTXT.qtype = kDNSType_TXT; + query->qTXT.qclass = kDNSClass_IN; + query->qTXT.LongLived = mDNSfalse; + query->qTXT.ExpectUnique = mDNStrue; + query->qTXT.ForceMCast = mDNSfalse; + query->qTXT.ReturnIntermed = mDNSfalse; + query->qTXT.SuppressUnusable = mDNSfalse; + query->qTXT.DenyOnCellInterface = mDNSfalse; + query->qTXT.DenyOnExpInterface = mDNSfalse; + query->qTXT.SearchListIndex = 0; + query->qTXT.AppendSearchDomains = 0; + query->qTXT.RetryWithSearchDomains = mDNSfalse; + query->qTXT.TimeoutQuestion = 0; + query->qTXT.WakeOnResolve = 0; + query->qTXT.UseBackgroundTrafficClass = mDNSfalse; + query->qTXT.ValidationRequired = 0; + query->qTXT.ValidatingResponse = 0; + query->qTXT.ProxyQuestion = 0; + query->qTXT.qnameOrig = mDNSNULL; + query->qTXT.AnonInfo = mDNSNULL; + query->qTXT.QuestionCallback = FoundServiceInfoTXT; + query->qTXT.QuestionContext = query; + + query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qAv4.InterfaceID = info->InterfaceID; + query->qAv4.flags = 0; + query->qAv4.Target = zeroAddr; + query->qAv4.qname.c[0] = 0; + query->qAv4.qtype = kDNSType_A; + query->qAv4.qclass = kDNSClass_IN; + query->qAv4.LongLived = mDNSfalse; + query->qAv4.ExpectUnique = mDNStrue; + query->qAv4.ForceMCast = mDNSfalse; + query->qAv4.ReturnIntermed = mDNSfalse; + query->qAv4.SuppressUnusable = mDNSfalse; + query->qAv4.DenyOnCellInterface = mDNSfalse; + query->qAv4.DenyOnExpInterface = mDNSfalse; + query->qAv4.SearchListIndex = 0; + query->qAv4.AppendSearchDomains = 0; + query->qAv4.RetryWithSearchDomains = mDNSfalse; + query->qAv4.TimeoutQuestion = 0; + query->qAv4.WakeOnResolve = 0; + query->qAv4.UseBackgroundTrafficClass = mDNSfalse; + query->qAv4.ValidationRequired = 0; + query->qAv4.ValidatingResponse = 0; + query->qAv4.ProxyQuestion = 0; + query->qAv4.qnameOrig = mDNSNULL; + query->qAv4.AnonInfo = mDNSNULL; + query->qAv4.QuestionCallback = FoundServiceInfo; + query->qAv4.QuestionContext = query; + + query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qAv6.InterfaceID = info->InterfaceID; + query->qAv6.flags = 0; + query->qAv6.Target = zeroAddr; + query->qAv6.qname.c[0] = 0; + query->qAv6.qtype = kDNSType_AAAA; + query->qAv6.qclass = kDNSClass_IN; + query->qAv6.LongLived = mDNSfalse; + query->qAv6.ExpectUnique = mDNStrue; + query->qAv6.ForceMCast = mDNSfalse; + query->qAv6.ReturnIntermed = mDNSfalse; + query->qAv6.SuppressUnusable = mDNSfalse; + query->qAv6.DenyOnCellInterface = mDNSfalse; + query->qAv6.DenyOnExpInterface = mDNSfalse; + query->qAv6.SearchListIndex = 0; + query->qAv6.AppendSearchDomains = 0; + query->qAv6.RetryWithSearchDomains = mDNSfalse; + query->qAv6.TimeoutQuestion = 0; + query->qAv6.UseBackgroundTrafficClass = mDNSfalse; + query->qAv6.ValidationRequired = 0; + query->qAv6.ValidatingResponse = 0; + query->qAv6.ProxyQuestion = 0; + query->qAv6.qnameOrig = mDNSNULL; + query->qAv6.AnonInfo = mDNSNULL; + query->qAv6.QuestionCallback = FoundServiceInfo; + query->qAv6.QuestionContext = query; + + query->GotSRV = mDNSfalse; + query->GotTXT = mDNSfalse; + query->GotADD = mDNSfalse; + query->Answers = 0; + + query->info = info; + query->ServiceInfoQueryCallback = Callback; + query->ServiceInfoQueryContext = Context; // info->name = Must already be set up by client // info->interface = Must already be set up by client - info->ip = zeroAddr; - info->port = zeroIPPort; - info->TXTlen = 0; + 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); + // 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); - } + 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); - } +{ + mDNS_Lock(m); + // We use mDNS_StopQuery_internal here because we're already holding the lock + if (q->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qSRV); + if (q->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qTXT); + if (q->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv4); + if (q->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv6); + mDNS_Unlock(m); +} mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, - const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) - { - question->InterfaceID = InterfaceID; - question->Target = zeroAddr; - question->qtype = kDNSType_PTR; - question->qclass = kDNSClass_IN; - question->LongLived = mDNSfalse; - question->ExpectUnique = mDNSfalse; - question->ForceMCast = mDNSfalse; - question->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)); - } + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) +{ + question->InterfaceID = InterfaceID; + question->flags = 0; + question->Target = zeroAddr; + question->qtype = kDNSType_PTR; + question->qclass = kDNSClass_IN; + question->LongLived = mDNSfalse; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = mDNSfalse; + question->ReturnIntermed = mDNSfalse; + question->SuppressUnusable = mDNSfalse; + question->DenyOnCellInterface = mDNSfalse; + question->DenyOnExpInterface = mDNSfalse; + question->SearchListIndex = 0; + question->AppendSearchDomains = 0; + question->RetryWithSearchDomains = mDNSfalse; + question->TimeoutQuestion = 0; + question->WakeOnResolve = 0; + question->UseBackgroundTrafficClass = mDNSfalse; + question->ValidationRequired = 0; + question->ValidatingResponse = 0; + question->ProxyQuestion = 0; + question->qnameOrig = mDNSNULL; + question->AnonInfo = mDNSNULL; + question->pid = mDNSPlatformGetPID(); + 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 @@ -6260,567 +12381,849 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m #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); - } +{ + 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) - { + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) +{ + if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) + { + LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); + return(mStatus_Invalid); + } + + mDNS_Lock(m); + + // If TTL is unspecified, leave TTL unchanged + if (newttl == 0) newttl = rr->resrec.rroriginalttl; + + // If we already have an update queued up which has not gone through yet, give the client a chance to free that memory + if (rr->NewRData) + { + RData *n = rr->NewRData; + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, n, rr->newrdlength); // ...and let the client free this memory, if necessary + } + + rr->NewRData = newrdata; + rr->newrdlength = newrdlength; + rr->UpdateCallback = Callback; + #ifndef UNICAST_DISABLED - mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name)); -#else - mDNSBool unicast = mDNSfalse; + if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P && !IsLocalDomain(rr->resrec.name)) + { + mStatus status = uDNS_UpdateRecord(m, rr); + // The caller frees the memory on error, don't retain stale pointers + if (status != mStatus_NoError) { rr->NewRData = mDNSNULL; rr->newrdlength = 0; } + mDNS_Unlock(m); + return(status); + } #endif - if (!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 + if (RRLocalOnly(rr) || (rr->resrec.rroriginalttl == newttl && + rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))) + CompleteRDataUpdate(m, rr); + else + { + rr->AnnounceCount = InitialAnnounceCount; + InitializeLastAPTime(m, rr); + while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); + if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; + if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval); + if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); + if (rr->UpdateCredits <= 5) + { + mDNSu32 delay = 6 - rr->UpdateCredits; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum + if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + (mDNSs32)delay * mDNSPlatformOneSecond); + rr->ThisAPInterval *= 4; + rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval; + LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", + rr->resrec.name->c, delay, delay > 1 ? "s" : ""); + } + rr->resrec.rroriginalttl = newttl; + } + + mDNS_Unlock(m); + return(mStatus_NoError); +} + +// Note: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) - { - mStatus status; - mDNS_Lock(m); - status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - mDNS_Unlock(m); - return(status); - } +{ + 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); +// Circular reference: AdvertiseInterface references mDNS_HostNameCallback, which calls mDNS_SetFQDN, which call AdvertiseInterface +mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) - { - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) break; - return(intf); - } +{ + 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 +{ + char buffer[MAX_REVERSE_MAPPING_NAME]; + NetworkInterfaceInfo *primary; + + if (!set->McastTxRx) + { + LogInfo("AdvertiseInterface: Returning, not multicast capable %s", set->ifname); + return; + } +#if TARGET_OS_EMBEDDED + if (!m->AutoTargetServices) + { + LogInfo("AdvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname); + return; + } +#endif + + 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 interface is marked as a direct link, we can assume the address record is unique + // and does not need to go through the probe phase of the probe/announce packet sequence. + mDNSu8 recordType = (set->DirectLink ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique); + + if (set->DirectLink) + LogInfo("AdvertiseInterface: Marking address record as kDNSRecordTypeKnownUnique for %s", set->ifname); + + // Send dynamic update for non-linklocal IPv4 Addresses + mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, recordType, AuthRecordAny, mDNS_HostNameCallback, set); + mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); #if ANSWER_REMOTE_HOSTNAME_QUERIES - set->RR_A .AllowRemoteQuery = mDNStrue; - set->RR_PTR .AllowRemoteQuery = mDNStrue; - set->RR_HINFO.AllowRemoteQuery = mDNStrue; + 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; - } - } + // 1. Set up Address record to map from host name ("foo.local.") to IP address + // 2. Set up reverse-lookup PTR record to map from our address back to our host name + AssignDomainName(&set->RR_A.namestorage, &m->MulticastHostname); + if (set->ip.type == mDNSAddrType_IPv4) + { + set->RR_A.resrec.rrtype = kDNSType_A; + set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", + set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); + } + else if (set->ip.type == mDNSAddrType_IPv6) + { + int i; + set->RR_A.resrec.rrtype = kDNSType_AAAA; + set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6; + for (i = 0; i < 16; i++) + { + static const char hexValues[] = "0123456789ABCDEF"; + buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F]; + buffer[i * 4 + 1] = '.'; + buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4]; + buffer[i * 4 + 3] = '.'; + } + mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); + } + + MakeDomainNameFromDNSNameString(&set->RR_PTR.namestorage, buffer); + set->RR_PTR.AutoTarget = Target_AutoHost; // Tell mDNS that the target of this PTR is to be kept in sync with our host name + set->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server + + set->RR_A.RRSet = &primary->RR_A; // May refer to self + + mDNS_Register_internal(m, &set->RR_A); + mDNS_Register_internal(m, &set->RR_PTR); + +#if APPLE_OSX_mDNSResponder + // must be after the mDNS_Register_internal() calls so that records have complete rdata fields, etc + D2D_start_advertising_interface(set); +#endif // APPLE_OSX_mDNSResponder + + if (!NO_HINFO && m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254) + { + mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data; + AssignDomainName(&set->RR_HINFO.namestorage, &m->MulticastHostname); + set->RR_HINFO.DependentOn = &set->RR_A; + mDNSPlatformMemCopy(p, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); + p += 1 + (int)p[0]; + mDNSPlatformMemCopy(p, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); + mDNS_Register_internal(m, &set->RR_HINFO); + } + else + { + debugf("Not creating HINFO record: platform support layer provided no information"); + set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered; + } +} mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) - { - NetworkInterfaceInfo *intf; - +{ + 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); - } + NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); + AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->RR_A.RRSet == &set->RR_A) + intf->RR_A.RRSet = A; + + // Unregister these records. + // When doing the mDNS_Exit processing, we first call DeadvertiseInterface for each interface, so by the time the platform + // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. + // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. + // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). + if (set->RR_A.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); + if (set->RR_PTR.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); + if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); + +#if APPLE_OSX_mDNSResponder + D2D_stop_advertising_interface(set); +#endif // APPLE_OSX_mDNSResponder + +} + +mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m) +{ + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->Advertise) + { + LogInfo("AdvertiseInterface: Advertising for ifname %s", intf->ifname); + AdvertiseInterface(m, intf); + } + } +} + +mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m) +{ +#if TARGET_OS_EMBEDDED + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->Advertise) + { + LogInfo("DeadvertiseInterface: Deadvertising for ifname %s", intf->ifname); + DeadvertiseInterface(m, intf); + } + } +#else + (void) m; //unused +#endif +} 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); - } +{ + domainname newmname; + NetworkInterfaceInfo *intf; + AuthRecord *rr; + newmname.c[0] = 0; + + if (!AppendDomainLabel(&newmname, &m->hostlabel)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } + if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } + + mDNS_Lock(m); + + if (SameDomainNameCS(&m->MulticastHostname, &newmname)) debugf("mDNS_SetFQDN - hostname unchanged"); + else + { + AssignDomainName(&m->MulticastHostname, &newmname); + + // 1. Stop advertising our address records on all interfaces + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) DeadvertiseInterface(m, intf); + + // 2. Start advertising our address records using the new name + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) AdvertiseInterface(m, intf); + } + + // 3. Make sure that any AutoTarget SRV records (and the like) get updated + for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); + for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); + + mDNS_Unlock(m); +} + +mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + (void)rr; // Unused parameter + + #if MDNS_DEBUGMSGS + { + char *msg = "Unknown result"; + if (result == mStatus_NoError) msg = "Name registered"; + else if (result == mStatus_NameConflict) msg = "Name conflict"; + debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); + } + #endif + + if (result == mStatus_NoError) + { + // Notify the client that the host name is successfully registered + if (m->MainCallback) + m->MainCallback(m, mStatus_NoError); + } + else if (result == mStatus_NameConflict) + { + domainlabel oldlabel = m->hostlabel; + + // 1. First give the client callback a chance to pick a new name + if (m->MainCallback) + m->MainCallback(m, mStatus_NameConflict); + + // 2. If the client callback didn't do it, add (or increment) an index ourselves + // This needs to be case-INSENSITIVE compare, because we need to know that the name has been changed so as to + // remedy the conflict, and a name that differs only in capitalization will just suffer the exact same conflict again. + if (SameDomainLabel(m->hostlabel.c, oldlabel.c)) + IncrementLabelSuffix(&m->hostlabel, mDNSfalse); + + // 3. Generate the FQDNs from the hostlabel, + // and make sure all SRV records, etc., are updated to reference our new hostname + mDNS_SetFQDN(m); + LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c); + } + else if (result == mStatus_MemFree) + { + // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by + // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface + debugf("mDNS_HostNameCallback: MemFree (ignored)"); + } + else + LogMsg("mDNS_HostNameCallback: Unknown error %d for registration of record %s", result, rr->resrec.name->c); +} mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) - { - NetworkInterfaceInfo *intf; - active->IPv4Available = mDNSfalse; - active->IPv6Available = mDNSfalse; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->InterfaceID == active->InterfaceID) - { - if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue; - if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue; - } - } +{ + NetworkInterfaceInfo *intf; + active->IPv4Available = mDNSfalse; + active->IPv6Available = mDNSfalse; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == active->InterfaceID) + { + if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue; + if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue; + } +} + +mDNSlocal void RestartRecordGetZoneData(mDNS * const m) +{ + AuthRecord *rr; + LogInfo("RestartRecordGetZoneData: ResourceRecords"); + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (AuthRecord_uDNS(rr) && rr->state != regState_NoTarget) + { + debugf("RestartRecordGetZoneData: StartGetZoneData for %##s", rr->resrec.name->c); + // Zero out the updateid so that if we have a pending response from the server, it won't + // be accepted as a valid response. If we accept the response, we might free the new "nta" + if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); } + rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr); + } +} + +mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set) +{ + int i; + // We initialize ThisQInterval to -1 indicating that the question has not been started + // yet. If the question (browse) is started later during interface registration, it will + // be stopped during interface deregistration. We can't sanity check to see if the + // question has been stopped or not before initializing it to -1 because we need to + // initialize it to -1 the very first time. + + set->NetWakeBrowse.ThisQInterval = -1; + for (i=0; i<3; i++) + { + set->NetWakeResolve[i].ThisQInterval = -1; + set->SPSAddr[i].type = mDNSAddrType_None; + } + set->NextSPSAttempt = -1; + set->NextSPSAttemptTime = m->timenow; +} + +mDNSexport void mDNS_ActivateNetWake_internal(mDNS *const m, NetworkInterfaceInfo *set) +{ + NetworkInterfaceInfo *p = m->HostInterfaces; + while (p && p != set) p=p->next; + if (!p) { LogMsg("mDNS_ActivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; } + + if (set->InterfaceActive) + { + LogSPS("ActivateNetWake for %s (%#a)", set->ifname, &set->ip); + mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, mDNSNULL, set->InterfaceID, 0, mDNSfalse, mDNSfalse, m->SPSBrowseCallback, set); + } +} + +mDNSexport void mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceInfo *set) +{ + NetworkInterfaceInfo *p = m->HostInterfaces; + while (p && p != set) p=p->next; + if (!p) { LogMsg("mDNS_DeactivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; } + + // Note: We start the browse only if the interface is NetWake capable and we use this to + // stop the resolves also. Hence, the resolves should not be started without the browse + // being started i.e, resolves should not happen unless NetWake capable which is + // guaranteed by BeginSleepProcessing. + if (set->NetWakeBrowse.ThisQInterval >= 0) + { + int i; + LogSPS("DeactivateNetWake for %s (%#a)", set->ifname, &set->ip); + + // Stop our browse and resolve operations + mDNS_StopQuery_internal(m, &set->NetWakeBrowse); + for (i=0; i<3; i++) if (set->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery_internal(m, &set->NetWakeResolve[i]); + + // Make special call to the browse callback to let it know it can to remove all records for this interface + if (m->SPSBrowseCallback) + { + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + m->SPSBrowseCallback(m, &set->NetWakeBrowse, mDNSNULL, QC_rmv); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + + // Reset our variables back to initial state, so we're ready for when NetWake is turned back on + // (includes resetting NetWakeBrowse.ThisQInterval back to -1) + InitializeNetWakeState(m, set); + } +} mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping) - { - mDNSBool FirstOfType = mDNStrue; - NetworkInterfaceInfo **p = &m->HostInterfaces; +{ + AuthRecord *rr; + mDNSBool FirstOfType = mDNStrue; + NetworkInterfaceInfo **p = &m->HostInterfaces; + + if (!set->InterfaceID) + { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } + + if (!mDNSAddressIsValidNonZero(&set->mask)) + { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); } + + mDNS_Lock(m); + + // Assume this interface will be active now, unless we find a duplicate already in the list + set->InterfaceActive = mDNStrue; + set->IPv4Available = (mDNSu8)(set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); + set->IPv6Available = (mDNSu8)(set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); + + InitializeNetWakeState(m, set); + + // Scan list to see if this InterfaceID is already represented + while (*p) + { + if (*p == set) + { + LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo that's already in the list"); + mDNS_Unlock(m); + return(mStatus_AlreadyRegistered); + } + + if ((*p)->InterfaceID == set->InterfaceID) + { + // This InterfaceID already represented by a different interface in the list, so mark this instance inactive for now + set->InterfaceActive = mDNSfalse; + if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse; + if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue; + if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue; + } + + p=&(*p)->next; + } + + set->next = mDNSNULL; + *p = set; + + if (set->Advertise) + AdvertiseInterface(m, set); + + LogInfo("mDNS_RegisterInterface: InterfaceID %d %s (%#a) %s", + (uint32_t)set->InterfaceID, set->ifname, &set->ip, + set->InterfaceActive ? + "not represented in list; marking active and retriggering queries" : + "already represented in list; marking inactive for now"); + + if (set->NetWake) mDNS_ActivateNetWake_internal(m, set); + + // In early versions of OS X the IPv6 address remains on an interface even when the interface is turned off, + // giving the false impression that there's an active representative of this interface when there really isn't. + // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records, + // even if we believe that we previously had an active representative of this interface. + if (set->McastTxRx && (FirstOfType || set->InterfaceActive)) + { + DNSQuestion *q; + // Normally, after an interface comes up, we pause half a second before beginning probing. + // This is to guard against cases where there's rapid interface changes, where we could be confused by + // seeing packets we ourselves sent just moments ago (perhaps when this interface had a different address) + // which are then echoed back after a short delay by some Ethernet switches and some 802.11 base stations. + // We don't want to do a probe, and then see a stale echo of an announcement we ourselves sent, + // and think it's a conflicting answer to our probe. + // In the case of a flapping interface, we pause for five seconds, and reduce the announcement count to one packet. + const mDNSs32 probedelay = flapping ? mDNSPlatformOneSecond * 5 : mDNSPlatformOneSecond / 2; + const mDNSu8 numannounce = flapping ? (mDNSu8)1 : InitialAnnounceCount; + + // Use a small amount of randomness: + // In the case of a network administrator turning on an Ethernet hub so that all the + // connected machines establish link at exactly the same time, we don't want them all + // to go and hit the network with identical queries at exactly the same moment. + // We set a random delay of up to InitialQuestionInterval (1/3 second). + // We must *never* set m->SuppressSending to more than that (or set it repeatedly in a way + // that causes mDNSResponder to remain in a prolonged state of SuppressSending, because + // suppressing packet sending for more than about 1/3 second can cause protocol correctness + // to start to break down (e.g. we don't answer probes fast enough, and get name conflicts). + // See <rdar://problem/4073853> mDNS: m->SuppressSending set too enthusiastically + if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); + + if (flapping) + { + LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + m->mDNSStats.InterfaceUpFlap++; + } + + LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay); + if (m->SuppressProbes == 0 || + m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0) + m->SuppressProbes = NonZeroTime(m->timenow + probedelay); + + // Include OWNER option in packets for 60 seconds after connecting to the network. Setting + // it here also handles the wake up case as the network link comes UP after waking causing + // us to reconnect to the network. If we do this as part of the wake up code, it is possible + // that the network link comes UP after 60 seconds and we never set the OWNER option + m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); + LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner"); + + m->mDNSStats.InterfaceUp++; + for (q = m->Questions; q; q=q->next) // Scan our list of questions + { + if (mDNSOpaque16IsZero(q->TargetQID)) + { + if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, + { // then reactivate this question + // If flapping, delay between first and second queries is nine seconds instead of one second + mDNSBool dodelay = flapping && (q->FlappingInterface1 == set->InterfaceID || q->FlappingInterface2 == set->InterfaceID); + mDNSs32 initial = dodelay ? InitialQuestionInterval * QuestionIntervalStep2 : InitialQuestionInterval; + mDNSs32 qdelay = dodelay ? mDNSPlatformOneSecond * 5 : 0; + if (dodelay) LogInfo("No cache records expired for %##s (%s); okay to delay questions a little", q->qname.c, DNSTypeName(q->qtype)); + + if (!q->ThisQInterval || q->ThisQInterval > initial) + { + q->ThisQInterval = initial; + +#if mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; +#else // mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = SET_QU_IN_FIRST_QUERY; +#endif // mDNS_REQUEST_UNICAST_RESPONSE + + } + q->LastQTime = m->timenow - q->ThisQInterval + qdelay; + q->RecentAnswerPkts = 0; + // Change the salt + ReInitAnonInfo(&q->AnonInfo, &q->qname); + 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) + { + // Change the salt + ReInitAnonInfo(&rr->resrec.AnonInfo, rr->resrec.name); + mDNSCoreRestartRegistration(m, rr, numannounce); + } + } +#if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE + DNSSECProbe(m); +#endif + } - if (!set->InterfaceID) - { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } + RestartRecordGetZoneData(m); - if (!mDNSAddressIsValidNonZero(&set->mask)) - { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); } + mDNS_UpdateAllowSleep(m); - 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); + mDNS_Unlock(m); + return(mStatus_NoError); +} - 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 +// 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); - } +{ + NetworkInterfaceInfo **p = &m->HostInterfaces; + mDNSBool revalidate = mDNSfalse; + + mDNS_Lock(m); + + // Find this record in our list + while (*p && *p != set) p=&(*p)->next; + if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; } + + mDNS_DeactivateNetWake_internal(m, set); + + // Unlink this record from our list + *p = (*p)->next; + set->next = mDNSNULL; + + if (!set->InterfaceActive) + { + // If this interface not the active member of its set, update the v4/v6Available flags for the active member + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) + UpdateInterfaceProtocols(m, intf); + } + else + { + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, set->InterfaceID); + if (intf) + { + LogInfo("mDNS_DeregisterInterface: Another representative of InterfaceID %d %s (%#a) exists;" + " making it active", (uint32_t)set->InterfaceID, set->ifname, &set->ip); + if (intf->InterfaceActive) + LogMsg("mDNS_DeregisterInterface: ERROR intf->InterfaceActive already set for %s (%#a)", set->ifname, &set->ip); + intf->InterfaceActive = mDNStrue; + UpdateInterfaceProtocols(m, intf); + + if (intf->NetWake) mDNS_ActivateNetWake_internal(m, intf); + + // See if another representative *of the same type* exists. If not, we mave have gone from + // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid. + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type) + break; + if (!intf) revalidate = mDNStrue; + } + else + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + DNSQuestion *q; + + LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %d %s (%#a) deregistered;" + " marking questions etc. dormant", (uint32_t)set->InterfaceID, set->ifname, &set->ip); + + m->mDNSStats.InterfaceDown++; + + if (set->McastTxRx && flapping) + { + LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + m->mDNSStats.InterfaceDownFlap++; + } + + // 1. Deactivate any questions specific to this interface, and tag appropriate questions + // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them + for (q = m->Questions; q; q=q->next) + { + if (q->InterfaceID == set->InterfaceID) q->ThisQInterval = 0; + if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) + { + q->FlappingInterface2 = q->FlappingInterface1; + q->FlappingInterface1 = set->InterfaceID; // Keep history of the last two interfaces to go away + } + } + + // 2. Flush any cache records received on this interface + revalidate = mDNSfalse; // Don't revalidate if we're flushing the records + FORALL_CACHERECORDS(slot, cg, rr) + { + if (rr->resrec.InterfaceID == set->InterfaceID) + { + // If this interface is deemed flapping, + // postpone deleting the cache records in case the interface comes back again + if (set->McastTxRx && flapping) + { + // For a flapping interface we want these record to go away after 30 seconds + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); + // We set UnansweredQueries = MaxUnansweredQueries so we don't waste time doing any queries for them -- + // if the interface does come back, any relevant questions will be reactivated anyway + rr->UnansweredQueries = MaxUnansweredQueries; + } + else + { + mDNS_PurgeCacheResourceRecord(m, rr); + } + } + } + } + } + + // If we were advertising on this interface, deregister those address and reverse-lookup records now + if (set->Advertise) DeadvertiseInterface(m, set); + + // If we have any cache records received on this interface that went away, then re-verify them. + // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, + // giving the false impression that there's an active representative of this interface when there really isn't. + // Don't need to do this when shutting down, because *all* interfaces are about to go away + if (revalidate && !m->ShutdownTime) + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + FORALL_CACHERECORDS(slot, cg, rr) + if (rr->resrec.InterfaceID == set->InterfaceID) + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); + } + + mDNS_UpdateAllowSleep(m); + + mDNS_Unlock(m); +} + +mDNSlocal void SetAnonInfoSRS(ServiceRecordSet *sr, int NumSubTypes) +{ + int i, len; + + if (!sr->AnonData) + return; + + len = mDNSPlatformStrLen(sr->AnonData); + if (sr->RR_PTR.resrec.AnonInfo) + { + LogMsg("SetAnonInfoSRS: Freeing AnonInfo for PTR record %##s, should have been freed already", sr->RR_PTR.resrec.name->c); + FreeAnonInfo(sr->RR_PTR.resrec.AnonInfo); + } + sr->RR_PTR.resrec.AnonInfo = AllocateAnonInfo(sr->RR_PTR.resrec.name, sr->AnonData, len, mDNSNULL); + for (i=0; i<NumSubTypes; i++) + { + if (sr->SubTypes[i].resrec.AnonInfo) + { + LogMsg("SetAnonInfoSRS: Freeing AnonInfo for subtype record %##s, should have been freed already", sr->SubTypes[i].resrec.name->c); + FreeAnonInfo(sr->SubTypes[i].resrec.AnonInfo); + } + sr->SubTypes[i].resrec.AnonInfo = AllocateAnonInfo(sr->SubTypes[i].resrec.name, sr->AnonData, len, mDNSNULL); + } +} + +mDNSlocal void ResetAnonInfoSRS(ServiceRecordSet *sr, int NumSubTypes) +{ + int i; + + if (!sr->AnonData) + return; + if (sr->RR_PTR.resrec.AnonInfo) + { + FreeAnonInfo(sr->RR_PTR.resrec.AnonInfo); + sr->RR_PTR.resrec.AnonInfo = mDNSNULL; + } + for (i=0; i<NumSubTypes; i++) + { + if (sr->SubTypes[i].resrec.AnonInfo) + { + FreeAnonInfo(sr->SubTypes[i].resrec.AnonInfo); + sr->SubTypes[i].resrec.AnonInfo = mDNSNULL; + } + } +} 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); - } +{ + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + (void)m; // Unused parameter + + #if MDNS_DEBUGMSGS + { + char *msg = "Unknown result"; + if (result == mStatus_NoError) msg = "Name Registered"; + else if (result == mStatus_NameConflict) msg = "Name Conflict"; + else if (result == mStatus_MemFree) msg = "Memory Free"; + debugf("ServiceCallback: %##s (%s) %s (%d)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); + } + #endif + + // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing) + if (result == mStatus_NoError && rr != &sr->RR_SRV) return; + + // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that + if (result == mStatus_NameConflict) + { + sr->Conflict = mDNStrue; // Record that this service set had a conflict + mDNS_DeregisterService(m, sr); // Unlink the records from our list + return; + } + + if (result == mStatus_MemFree) + { + // If the SRV/TXT/PTR records, or the _services._dns-sd._udp record, or any of the subtype PTR records, + // are still in the process of deregistering, don't pass on the NameConflict/MemFree message until + // every record is finished cleaning up. + mDNSu32 i; + ExtraResourceRecord *e = sr->Extras; + + if (sr->RR_SRV.resrec.RecordType != kDNSRecordTypeUnregistered) return; + if (sr->RR_TXT.resrec.RecordType != kDNSRecordTypeUnregistered) return; + if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return; + if (sr->RR_ADV.resrec.RecordType != kDNSRecordTypeUnregistered) return; + for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return; + + while (e) + { + if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return; + e = e->next; + } + ResetAnonInfoSRS(sr, sr->NumSubTypes); + + // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, + // then we can now report the NameConflict to the client + if (sr->Conflict) result = mStatus_NameConflict; + + } + + LogInfo("ServiceCallback: All records %s for %##s", (result == mStatus_MemFree ? "Unregistered" : "Registered"), sr->RR_PTR.resrec.name->c); + // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback + // function is allowed to do anything, including deregistering this service and freeing its memory. + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); +} mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; - if (sr->ServiceCallback) - sr->ServiceCallback(m, sr, result); - } +{ + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); +} + + +mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags) +{ + AuthRecType artype; + + if (InterfaceID == mDNSInterface_LocalOnly) + artype = AuthRecordLocalOnly; + else if (InterfaceID == mDNSInterface_P2P) + artype = AuthRecordP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P) + && (flags & coreFlagIncludeAWDL)) + artype = AuthRecordAnyIncludeAWDLandP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P)) + artype = AuthRecordAnyIncludeP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeAWDL)) + artype = AuthRecordAnyIncludeAWDL; + else + artype = AuthRecordAny; + + return artype; +} // Note: // Name is first label of domain name (any dots in the name are actual dots, not label separators) @@ -6830,308 +13233,304 @@ mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) // left waiting forever looking for a nonexistent record.) // If the host parameter is mDNSNULL or the root domain (ASCII NUL), // then the default host name (m->MulticastHostname) is automatically used +// If the optional target host parameter is set, then the storage it points to must remain valid for the lifetime of the service registration mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, - const domainlabel *const name, const domainname *const type, const domainname *const domain, - const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, - AuthRecord *SubTypes, mDNSu32 NumSubTypes, - 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); - } + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags) +{ + mStatus err; + mDNSu32 i; + mDNSu32 hostTTL; + AuthRecType artype; + mDNSu8 recordType = (flags & coreFlagKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique; + + sr->ServiceCallback = Callback; + sr->ServiceContext = Context; + sr->Conflict = mDNSfalse; + + sr->Extras = mDNSNULL; + sr->NumSubTypes = NumSubTypes; + sr->SubTypes = SubTypes; + sr->flags = flags; + + artype = setAuthRecType(InterfaceID, flags); + + // Initialize the AuthRecord objects to sane values + // Need to initialize everything correctly *before* making the decision whether to do a RegisterNoSuchService and bail out + mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, artype, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr); + + if (flags & coreFlagWakeOnly) + { + sr->RR_PTR.AuthFlags = AuthFlagsWakeOnly; + } + + if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp")) + hostTTL = kHostNameSmallTTL; + else + hostTTL = kHostNameTTL; + + mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, artype, ServiceCallback, sr); + + // If port number is zero, that means the client is really trying to do a RegisterNoSuchService + if (mDNSIPPortIsZero(port)) + return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, InterfaceID, NSSCallback, sr, flags)); + + // If the client is registering an oversized TXT record, + // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it + if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) + sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; + + // Set up the record names + // For now we only create an advisory record for the main type, not for subtypes + // We need to gain some operational experience before we decide if there's a need to create them for subtypes too + if (ConstructServiceName(&sr->RR_ADV.namestorage, (const domainlabel*)"\x09_services", (const domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL) + return(mStatus_BadParamErr); + if (ConstructServiceName(&sr->RR_PTR.namestorage, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + if (ConstructServiceName(&sr->RR_SRV.namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + AssignDomainName(&sr->RR_TXT.namestorage, sr->RR_SRV.resrec.name); + + // 1. Set up the ADV record rdata to advertise our service type + AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name); + + // 2. Set up the PTR record rdata to point to our service name + // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too + // Note: uDNS registration code assumes that Additional1 points to the SRV record + AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name); + sr->RR_PTR.Additional1 = &sr->RR_SRV; + sr->RR_PTR.Additional2 = &sr->RR_TXT; + + // 2a. Set up any subtype PTRs to point to our service name + // If the client is using subtypes, it is the client's responsibility to have + // already set the first label of the record name to the subtype being registered + for (i=0; i<NumSubTypes; i++) + { + domainname st; + AssignDomainName(&st, sr->SubTypes[i].resrec.name); + st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService()) + AppendDomainName(&st, type); + mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr); + if (ConstructServiceName(&sr->SubTypes[i].namestorage, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr); + AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, &sr->RR_SRV.namestorage); + sr->SubTypes[i].Additional1 = &sr->RR_SRV; + sr->SubTypes[i].Additional2 = &sr->RR_TXT; + } + + SetAnonInfoSRS(sr, NumSubTypes); + + // 3. Set up the SRV record rdata. + sr->RR_SRV.resrec.rdata->u.srv.priority = 0; + sr->RR_SRV.resrec.rdata->u.srv.weight = 0; + sr->RR_SRV.resrec.rdata->u.srv.port = port; + + // Setting AutoTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name + if (host && host->c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, host); + else { sr->RR_SRV.AutoTarget = Target_AutoHost; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; } + + // 4. Set up the TXT record rdata, + // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us + // Note: uDNS registration code assumes that DependentOn points to the SRV record + if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0; + else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c) + { + sr->RR_TXT.resrec.rdlength = txtlen; + if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr); + mDNSPlatformMemCopy(sr->RR_TXT.resrec.rdata->u.txt.c, txtinfo, txtlen); + } + sr->RR_TXT.DependentOn = &sr->RR_SRV; + + mDNS_Lock(m); + // It is important that we register SRV first. uDNS assumes that SRV is registered first so + // that if the SRV cannot find a target, rest of the records that belong to this service + // will not be activated. + err = mDNS_Register_internal(m, &sr->RR_SRV); + // If we can't register the SRV record due to errors, bail out. It has not been inserted in + // any list and hence no need to deregister. We could probably do similar checks for other + // records below and bail out. For now, this seems to be sufficient to address rdar://9304275 + if (err) + { + mDNS_Unlock(m); + return err; + } + if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); + // We register the RR_PTR last, because we want to be sure that in the event of a forced call to + // mDNS_StartExit, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers + // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to + // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to + // make sure we've deregistered all our records and done any other necessary cleanup before that happens. + if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV); + for (i=0; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[i]); + if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR); + + mDNS_Unlock(m); + + if (err) mDNS_DeregisterService(m, sr); + return(err); +} mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, - ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl) - { - 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; + ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 flags) +{ + ExtraResourceRecord **e; + mStatus status; + AuthRecType artype; + mDNSInterfaceID InterfaceID = sr->RR_PTR.resrec.InterfaceID; - if (ttl == 0) ttl = kStandardTTL; + artype = setAuthRecType(InterfaceID, flags); - 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); - } + extra->next = mDNSNULL; + mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, + extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, artype, ServiceCallback, sr); + AssignDomainName(&extra->r.namestorage, sr->RR_SRV.resrec.name); -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); - } + mDNS_Lock(m); + e = &sr->Extras; + while (*e) e = &(*e)->next; -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 (ttl == 0) ttl = kStandardTTL; - 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); - } + extra->r.DependentOn = &sr->RR_SRV; + + debugf("mDNS_AddRecordToService adding record to %##s %s %d", + extra->r.resrec.name->c, DNSTypeName(extra->r.resrec.rrtype), extra->r.resrec.rdlength); + + status = mDNS_Register_internal(m, &extra->r); + if (status == mStatus_NoError) *e = extra; + + mDNS_Unlock(m); + return(status); +} + +mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, + mDNSRecordCallback MemFreeCallback, void *Context) +{ + ExtraResourceRecord **e; + mStatus status; + + mDNS_Lock(m); + e = &sr->Extras; + while (*e && *e != extra) e = &(*e)->next; + if (!*e) + { + debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c); + status = mStatus_BadReferenceErr; + } + else + { + debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c); + extra->r.RecordCallback = MemFreeCallback; + extra->r.RecordContext = Context; + *e = (*e)->next; + status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); + } + mDNS_Unlock(m); + return(status); +} -// NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback, +mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname) +{ + // Note: Don't need to use mDNS_Lock(m) here, because this code is just using public routines + // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally. + domainlabel name1, name2; + domainname type, domain; + const domainname *host = sr->RR_SRV.AutoTarget ? mDNSNULL : &sr->RR_SRV.resrec.rdata->u.srv.target; + ExtraResourceRecord *extras = sr->Extras; + mStatus err; + + DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain); + if (!newname) + { + name2 = name1; + IncrementLabelSuffix(&name2, mDNStrue); + newname = &name2; + } + + if (SameDomainName(&domain, &localdomain)) + debugf("%##s service renamed from \"%#s\" to \"%#s\"", type.c, name1.c, newname->c); + else debugf("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c); + + err = mDNS_RegisterService(m, sr, newname, &type, &domain, + host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, + sr->SubTypes, sr->NumSubTypes, + sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, sr->flags); + + // mDNS_RegisterService() just reset sr->Extras to NULL. + // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run + // through the old list of extra records, and re-add them to our freshly created service registration + while (!err && extras) + { + ExtraResourceRecord *e = extras; + extras = extras->next; + err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl, 0); + } + + return(err); +} + +// Note: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSexport mStatus mDNS_DeregisterService(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); - } - } +mDNSexport mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt) +{ + // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() + if (mDNSIPPortIsZero(sr->RR_SRV.resrec.rdata->u.srv.port)) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); + + if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) + { + debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c); + return(mStatus_BadReferenceErr); + } + else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) + { + LogInfo("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c); + // Avoid race condition: + // If a service gets a conflict, then we set the Conflict flag to tell us to generate + // an mStatus_NameConflict message when we get the mStatus_MemFree for our PTR record. + // If the client happens to deregister the service in the middle of that process, then + // we clear the flag back to the normal state, so that we deliver a plain mStatus_MemFree + // instead of incorrectly promoting it to mStatus_NameConflict. + // This race condition is exposed particularly when the conformance test generates + // a whole batch of simultaneous conflicts across a range of services all advertised + // using the same system default name, and if we don't take this precaution then + // we end up incrementing m->nicelabel multiple times instead of just once. + // <rdar://problem/4060169> Bug when auto-renaming Computer Name after name collision + sr->Conflict = mDNSfalse; + return(mStatus_NoError); + } + else + { + mDNSu32 i; + mStatus status; + ExtraResourceRecord *e; + mDNS_Lock(m); + e = sr->Extras; + + // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the + // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay + mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat); + mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat); + + mDNS_Deregister_internal(m, &sr->RR_ADV, drt); + + // We deregister all of the extra records, but we leave the sr->Extras list intact + // in case the client wants to do a RenameAndReregister and reinstate the registration + while (e) + { + mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat); + e = e->next; + } + + for (i=0; i<sr->NumSubTypes; i++) + mDNS_Deregister_internal(m, &sr->SubTypes[i], drt); + + status = mDNS_Deregister_internal(m, &sr->RR_PTR, drt); + mDNS_Unlock(m); + return(status); + } +} // Create a registration that asserts that no such service exists with this name. // This can be useful where there is a given function is available through several protocols. @@ -7140,223 +13539,1540 @@ mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) // "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)); - } + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSu32 flags) +{ + AuthRecType artype; + + artype = setAuthRecType(InterfaceID, flags); + + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, artype, Callback, Context); + if (ConstructServiceName(&rr->namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + rr->resrec.rdata->u.srv.priority = 0; + rr->resrec.rdata->u.srv.weight = 0; + rr->resrec.rdata->u.srv.port = zeroIPPort; + if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host); + else rr->AutoTarget = Target_AutoHost; + return(mDNS_Register(m, rr)); +} mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, - mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) - { - 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)); - } + mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) +{ + AuthRecType artype; + + if (InterfaceID == mDNSInterface_LocalOnly) + artype = AuthRecordLocalOnly; + else if (InterfaceID == mDNSInterface_P2P) + artype = AuthRecordP2P; + else + artype = AuthRecordAny; + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, mDNSNULL, mDNSNULL); + if (!MakeDomainNameFromDNSNameString(&rr->namestorage, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); + if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); + return(mDNS_Register(m, rr)); +} + +mDNSlocal mDNSBool mDNS_IdUsedInResourceRecordsList(mDNS * const m, mDNSOpaque16 id) +{ + AuthRecord *r; + for (r = m->ResourceRecords; r; r=r->next) if (mDNSSameOpaque16(id, r->updateid)) return mDNStrue; + return mDNSfalse; +} + +mDNSlocal mDNSBool mDNS_IdUsedInQuestionsList(mDNS * const m, mDNSOpaque16 id) +{ + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) if (mDNSSameOpaque16(id, q->TargetQID)) return mDNStrue; + return mDNSfalse; +} + +mDNSexport mDNSOpaque16 mDNS_NewMessageID(mDNS * const m) +{ + mDNSOpaque16 id; + int i; + + for (i=0; i<10; i++) + { + id = mDNSOpaque16fromIntVal(1 + (mDNSu16)mDNSRandom(0xFFFE)); + if (!mDNS_IdUsedInResourceRecordsList(m, id) && !mDNS_IdUsedInQuestionsList(m, id)) break; + } + + debugf("mDNS_NewMessageID: %5d", mDNSVal16(id)); + + return id; +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - +#pragma mark - Sleep Proxy Server +#endif + +mDNSlocal void RestartARPProbing(mDNS *const m, AuthRecord *const rr) +{ + // If we see an ARP from a machine we think is sleeping, then either + // (i) the machine has woken, or + // (ii) it's just a stray old packet from before the machine slept + // To handle the second case, we reset ProbeCount, so we'll suppress our own answers for a while, to avoid + // generating ARP conflicts with a waking machine, and set rr->LastAPTime so we'll start probing again in 10 seconds. + // If the machine has just woken then we'll discard our records when we see the first new mDNS probe from that machine. + // If it was a stray old packet, then after 10 seconds we'll probe again and then start answering ARPs again. In this case we *do* + // need to send new ARP Announcements, because the owner's ARP broadcasts will have updated neighboring ARP caches, so we need to + // re-assert our (temporary) ownership of that IP address in order to receive subsequent packets addressed to that IPv4 address. + + rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForTypeUnique; + rr->ProbeRestartCount++; + + // If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably + // still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding. + // If we *have* started announcing, the machine is probably in the process of waking back up, so in that case + // we're more cautious and we wait ten seconds before probing it again. We do this because while waking from + // sleep, some network interfaces tend to lose or delay inbound packets, and without this delay, if the waking machine + // didn't answer our three probes within three seconds then we'd announce and cause it an unnecessary address conflict. + if (rr->AnnounceCount == InitialAnnounceCount && m->timenow - rr->LastAPTime >= 0) + InitializeLastAPTime(m, rr); + else + { + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = mDNSPlatformOneSecond; + rr->LastAPTime = m->timenow + mDNSPlatformOneSecond * 9; // Send first packet at rr->LastAPTime + rr->ThisAPInterval, i.e. 10 seconds from now + SetNextAnnounceProbeTime(m, rr); + } +} + +mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, const mDNSInterfaceID InterfaceID) +{ + static const mDNSOpaque16 ARP_op_request = { { 0, 1 } }; + AuthRecord *rr; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) return; + + mDNS_Lock(m); + + // Pass 1: + // Process ARP Requests and Probes (but not Announcements), and generate an ARP Reply if necessary. + // We also process ARPs from our own kernel (and 'answer' them by injecting a local ARP table entry) + // We ignore ARP Announcements here -- Announcements are not questions, they're assertions, so we don't need to answer them. + // The times we might need to react to an ARP Announcement are: + // (i) as an indication that the host in question has not gone to sleep yet (so we should delay beginning to proxy for it) or + // (ii) if it's a conflicting Announcement from another host + // -- and we check for these in Pass 2 below. + if (mDNSSameOpaque16(arp->op, ARP_op_request) && !mDNSSameIPv4Address(arp->spa, arp->tpa)) + { + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->tpa)) + { + static const char msg1[] = "ARP Req from owner -- re-probing"; + static const char msg2[] = "Ignoring ARP Request from "; + static const char msg3[] = "Creating Local ARP Cache entry "; + static const char msg4[] = "Answering ARP Request from "; + const char *const msg = mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC) ? msg1 : + (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : + mDNSSameEthAddress(&arp->sha, &intf->MAC) ? msg3 : msg4; + LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", + intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + if (msg == msg1) + { + if ( rr->ProbeRestartCount < MAX_PROBE_RESTARTS) + RestartARPProbing(m, rr); + else + LogSPS("Reached maximum number of restarts for probing - %s", ARDisplayString(m,rr)); + } + else if (msg == msg3) + { + mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + } + else if (msg == msg4) + { + SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha); + } + } + } + + // Pass 2: + // For all types of ARP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. + // (Strictly speaking we're only checking Announcement/Request/Reply packets, since ARP Probes have zero Sender IP address, + // so by definition (and by design) they can never conflict with any real (i.e. non-zero) IP address). + // We ignore ARPs we sent ourselves (Sender MAC address is our MAC address) because our own proxy ARPs do not constitute a conflict that we need to handle. + // If we see an apparently conflicting ARP, we check the sender hardware address: + // If the sender hardware address is the original owner this is benign, so we just suppress our own proxy answering for a while longer. + // If the sender hardware address is *not* the original owner, then this is a conflict, and we need to wake the sleeping machine to handle it. + if (mDNSSameEthAddress(&arp->sha, &intf->MAC)) + debugf("ARP from self for %.4a", &arp->tpa); + else + { + if (!mDNSSameIPv4Address(arp->spa, zerov4Addr)) + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa) && (rr->ProbeRestartCount < MAX_PROBE_RESTARTS)) + { + if (mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC)) + { + LogSPS("%-7s ARP from %.6a %.4a for %.4a -- Invalid H-MAC %.6a I-MAC %.6a %s", intf->ifname, + &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + } + else + { + RestartARPProbing(m, rr); + if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) + { + LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname, + mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", + &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); + } + else + { + LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, + &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } + } + } + } + + mDNS_Unlock(m); +} + +/* + // Option 1 is Source Link Layer Address Option + // Option 2 is Target Link Layer Address Option + mDNSlocal const mDNSEthAddr *GetLinkLayerAddressOption(const IPv6NDP *const ndp, const mDNSu8 *const end, mDNSu8 op) + { + const mDNSu8 *options = (mDNSu8 *)(ndp+1); + while (options < end) + { + debugf("NDP Option %02X len %2d %d", options[0], options[1], end - options); + if (options[0] == op && options[1] == 1) return (const mDNSEthAddr*)(options+2); + options += options[1] * 8; + } + return mDNSNULL; + } + */ + +mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, const mDNSv6Addr *spa, + const IPv6NDP *const ndp, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) +{ + AuthRecord *rr; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) return; + + mDNS_Lock(m); + + // Pass 1: Process Neighbor Solicitations, and generate a Neighbor Advertisement if necessary. + if (ndp->type == NDP_Sol) + { + //const mDNSEthAddr *const sha = GetLinkLayerAddressOption(ndp, end, NDP_SrcLL); + (void)end; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, ndp->target)) + { + static const char msg1[] = "NDP Req from owner -- re-probing"; + static const char msg2[] = "Ignoring NDP Request from "; + static const char msg3[] = "Creating Local NDP Cache entry "; + static const char msg4[] = "Answering NDP Request from "; + static const char msg5[] = "Answering NDP Probe from "; + const char *const msg = sha && mDNSSameEthAddress(sha, &rr->WakeUp.IMAC) ? msg1 : + (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : + sha && mDNSSameEthAddress(sha, &intf->MAC) ? msg3 : + spa && mDNSIPv6AddressIsZero(*spa) ? msg4 : msg5; + LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s", + intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + if (msg == msg1) + { + if (rr->ProbeRestartCount < MAX_PROBE_RESTARTS) + RestartARPProbing(m, rr); + else + LogSPS("Reached maximum number of restarts for probing - %s", ARDisplayString(m,rr)); + } + else if (msg == msg3) + mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + else if (msg == msg4) + SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha); + else if (msg == msg5) + SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + } + } + + // Pass 2: For all types of NDP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. + if (mDNSSameEthAddress(sha, &intf->MAC)) + debugf("NDP from self for %.16a", &ndp->target); + else + { + // For Neighbor Advertisements we check the Target address field, not the actual IPv6 source address. + // When a machine has both link-local and routable IPv6 addresses, it may send NDP packets making assertions + // about its routable IPv6 address, using its link-local address as the source address for all NDP packets. + // Hence it is the NDP target address we care about, not the actual packet source address. + if (ndp->type == NDP_Adv) spa = &ndp->target; + if (!mDNSSameIPv6Address(*spa, zerov6Addr)) + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa) && (rr->ProbeRestartCount < MAX_PROBE_RESTARTS)) + { + if (mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC)) + { + LogSPS("%-7s NDP from %.6a %.16a for %.16a -- Invalid H-MAC %.6a I-MAC %.6a %s", intf->ifname, + sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + } + else + { + RestartARPProbing(m, rr); + if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC)) + { + LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname, + ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr)); + } + else + { + LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, + sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } + } + } + } + + mDNS_Unlock(m); +} + +mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAddr *const sha, const mDNSAddr *const src, const mDNSAddr *const dst, const mDNSu8 protocol, + const mDNSu8 *const p, const TransportLayerPacket *const t, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID, const mDNSu16 len) +{ + const mDNSIPPort port = (protocol == 0x06) ? t->tcp.dst : (protocol == 0x11) ? t->udp.dst : zeroIPPort; + mDNSBool wake = mDNSfalse; + mDNSBool kaWake = mDNSfalse; + + switch (protocol) + { + #define XX wake ? "Received" : "Ignoring", end-p + case 0x01: LogSPS("Ignoring %d-byte ICMP from %#a to %#a", end-p, src, dst); + break; + + case 0x06: { + AuthRecord *kr; + mDNSu32 seq, ack; + #define TH_FIN 0x01 + #define TH_SYN 0x02 + #define TH_RST 0x04 + #define TH_ACK 0x10 + + kr = mDNS_MatchKeepaliveInfo(m, dst, src, port, t->tcp.src, &seq, &ack); + if (kr) + { + LogSPS("mDNSCoreReceiveRawTransportPacket: Found a Keepalive record from %#a:%d to %#a:%d", src, mDNSVal16(t->tcp.src), dst, mDNSVal16(port)); + // Plan to wake if + // (a) RST or FIN is set (the keepalive that we sent could have caused a reset) + // (b) packet that contains new data and acks a sequence number higher than the one + // we have been sending in the keepalive + + wake = ((t->tcp.flags & TH_RST) || (t->tcp.flags & TH_FIN)) ; + if (!wake) + { + mDNSu8 *ptr; + mDNSu32 pseq, pack; + mDNSBool data = mDNSfalse; + mDNSu8 tcphlen; + + // Convert to host order + ptr = (mDNSu8 *)&seq; + seq = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + ptr = (mDNSu8 *)&ack; + ack = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + pseq = t->tcp.seq; + ptr = (mDNSu8 *)&pseq; + pseq = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + pack = t->tcp.ack; + ptr = (mDNSu8 *)&pack; + pack = ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; + + // If the other side is acking one more than our sequence number (keepalive is one + // less than the last valid sequence sent) and it's sequence is more than what we + // acked before + //if (end - p - 34 - ((t->tcp.offset >> 4) * 4) > 0) data = mDNStrue; + tcphlen = ((t->tcp.offset >> 4) * 4); + if (end - ((mDNSu8 *)t + tcphlen) > 0) data = mDNStrue; + wake = ((int)(pack - seq) > 0) && ((int)(pseq - ack) >= 0) && data; + + // If we got a regular keepalive on a connection that was registed with the KeepAlive API, respond with an ACK + if ((t->tcp.flags & TH_ACK) && (data == mDNSfalse) && + ((int)(ack - pseq) == 1)) + { + // Send an ACK; + mDNS_SendKeepaliveACK(m, kr); + } + LogSPS("mDNSCoreReceiveRawTransportPacket: End %p, hlen %d, Datalen %d, pack %u, seq %u, pseq %u, ack %u, wake %d", + end, tcphlen, end - ((mDNSu8 *)t + tcphlen), pack, seq, pseq, ack, wake); + } + else { LogSPS("mDNSCoreReceiveRawTransportPacket: waking because of RST or FIN th_flags %d", t->tcp.flags); } + kaWake = wake; + } + else + { + // Plan to wake if + // (a) RST is not set, AND + // (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone. + wake = (!(t->tcp.flags & TH_RST) && (t->tcp.flags & (TH_FIN|TH_SYN)) != TH_FIN); + + // For now, to reduce spurious wakeups, we wake only for TCP SYN, + // except for ssh connections, where we'll wake for plain data packets too + if (!mDNSSameIPPort(port, SSHPort) && !(t->tcp.flags & 2)) wake = mDNSfalse; + + LogSPS("%s %d-byte TCP from %#a:%d to %#a:%d%s%s%s", XX, + src, mDNSVal16(t->tcp.src), dst, mDNSVal16(port), + (t->tcp.flags & 2) ? " SYN" : "", + (t->tcp.flags & 1) ? " FIN" : "", + (t->tcp.flags & 4) ? " RST" : ""); + } + break; + } + + case 0x11: { + #define ARD_AsNumber 3283 + static const mDNSIPPort ARD = { { ARD_AsNumber >> 8, ARD_AsNumber & 0xFF } }; + const mDNSu16 udplen = (mDNSu16)((mDNSu16)t->bytes[4] << 8 | t->bytes[5]); // Length *including* 8-byte UDP header + if (udplen >= sizeof(UDPHeader)) + { + const mDNSu16 datalen = udplen - sizeof(UDPHeader); + wake = mDNStrue; + + // For Back to My Mac UDP port 4500 (IPSEC) packets, we do some special handling + if (mDNSSameIPPort(port, IPSECPort)) + { + // Specifically ignore NAT keepalive packets + if (datalen == 1 && end >= &t->bytes[9] && t->bytes[8] == 0xFF) wake = mDNSfalse; + else + { + // Skip over the Non-ESP Marker if present + const mDNSBool NonESP = (end >= &t->bytes[12] && t->bytes[8] == 0 && t->bytes[9] == 0 && t->bytes[10] == 0 && t->bytes[11] == 0); + const IKEHeader *const ike = (IKEHeader *)(t + (NonESP ? 12 : 8)); + const mDNSu16 ikelen = datalen - (NonESP ? 4 : 0); + if (ikelen >= sizeof(IKEHeader) && end >= ((mDNSu8 *)ike) + sizeof(IKEHeader)) + if ((ike->Version & 0x10) == 0x10) + { + // ExchangeType == 5 means 'Informational' <http://www.ietf.org/rfc/rfc2408.txt> + // ExchangeType == 34 means 'IKE_SA_INIT' <http://www.iana.org/assignments/ikev2-parameters> + if (ike->ExchangeType == 5 || ike->ExchangeType == 34) wake = mDNSfalse; + LogSPS("%s %d-byte IKE ExchangeType %d", XX, ike->ExchangeType); + } + } + } + + // For now, because we haven't yet worked out a clean elegant way to do this, we just special-case the + // Apple Remote Desktop port number -- we ignore all packets to UDP 3283 (the "Net Assistant" port), + // except for Apple Remote Desktop's explicit manual wakeup packet, which looks like this: + // UDP header (8 bytes) + // Payload: 13 88 00 6a 41 4e 41 20 (8 bytes) ffffffffffff (6 bytes) 16xMAC (96 bytes) = 110 bytes total + if (mDNSSameIPPort(port, ARD)) wake = (datalen >= 110 && end >= &t->bytes[10] && t->bytes[8] == 0x13 && t->bytes[9] == 0x88); + + LogSPS("%s %d-byte UDP from %#a:%d to %#a:%d", XX, src, mDNSVal16(t->udp.src), dst, mDNSVal16(port)); + } + } + break; + + case 0x3A: if (&t->bytes[len] <= end) + { + mDNSu16 checksum = IPv6CheckSum(&src->ip.v6, &dst->ip.v6, protocol, t->bytes, len); + if (!checksum) mDNSCoreReceiveRawND(m, sha, &src->ip.v6, &t->ndp, &t->bytes[len], InterfaceID); + else LogInfo("IPv6CheckSum bad %04X %02X%02X from %#a to %#a", checksum, t->bytes[2], t->bytes[3], src, dst); + } + break; + + default: LogSPS("Ignoring %d-byte IP packet unknown protocol %d from %#a to %#a", end-p, protocol, src, dst); + break; + } + + if (wake) + { + AuthRecord *rr, *r2; + + mDNS_Lock(m); + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && + rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type && mDNSSameAddress(&rr->AddressProxy, dst)) + { + const mDNSu8 *const tp = (protocol == 6) ? (const mDNSu8 *)"\x4_tcp" : (const mDNSu8 *)"\x4_udp"; + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (r2->resrec.InterfaceID == InterfaceID && mDNSSameEthAddress(&r2->WakeUp.HMAC, &rr->WakeUp.HMAC) && + r2->resrec.RecordType != kDNSRecordTypeDeregistering && + r2->resrec.rrtype == kDNSType_SRV && mDNSSameIPPort(r2->resrec.rdata->u.srv.port, port) && + SameDomainLabel(ThirdLabel(r2->resrec.name)->c, tp)) + break; + if (!r2 && mDNSSameIPPort(port, IPSECPort)) r2 = rr; // So that we wake for BTMM IPSEC packets, even without a matching SRV record + if (!r2 && kaWake) r2 = rr; // So that we wake for keepalive packets, even without a matching SRV record + if (r2) + { + LogMsg("Waking host at %s %#a H-MAC %.6a I-MAC %.6a for %s", + InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, r2)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } + else + LogSPS("Sleeping host at %s %#a %.6a has no service on %#s %d", + InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, tp, mDNSVal16(port)); + } + mDNS_Unlock(m); + } +} + +mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) +{ + static const mDNSOpaque16 Ethertype_ARP = { { 0x08, 0x06 } }; // Ethertype 0x0806 = ARP + static const mDNSOpaque16 Ethertype_IPv4 = { { 0x08, 0x00 } }; // Ethertype 0x0800 = IPv4 + static const mDNSOpaque16 Ethertype_IPv6 = { { 0x86, 0xDD } }; // Ethertype 0x86DD = IPv6 + static const mDNSOpaque16 ARP_hrd_eth = { { 0x00, 0x01 } }; // Hardware address space (Ethernet = 1) + static const mDNSOpaque16 ARP_pro_ip = { { 0x08, 0x00 } }; // Protocol address space (IP = 0x0800) + + // Note: BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header. + // In other words, we can safely assume that pkt below (ARP, IPv4 or IPv6) is properly word aligned, + // but if pkt is 4-byte aligned, that necessarily means that eth CANNOT also be 4-byte aligned + // since it points to a an address 14 bytes before pkt. + const EthernetHeader *const eth = (const EthernetHeader *)p; + const NetworkLayerPacket *const pkt = (const NetworkLayerPacket *)(eth+1); + mDNSAddr src, dst; + #define RequiredCapLen(P) ((P)==0x01 ? 4 : (P)==0x06 ? 20 : (P)==0x11 ? 8 : (P)==0x3A ? 24 : 0) + + // Is ARP? Length must be at least 14 + 28 = 42 bytes + if (end >= p+42 && mDNSSameOpaque16(eth->ethertype, Ethertype_ARP) && mDNSSameOpaque16(pkt->arp.hrd, ARP_hrd_eth) && mDNSSameOpaque16(pkt->arp.pro, ARP_pro_ip)) + mDNSCoreReceiveRawARP(m, &pkt->arp, InterfaceID); + // Is IPv4 with zero fragmentation offset? Length must be at least 14 + 20 = 34 bytes + else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv4) && (pkt->v4.flagsfrags.b[0] & 0x1F) == 0 && pkt->v4.flagsfrags.b[1] == 0) + { + const mDNSu8 *const trans = p + 14 + (pkt->v4.vlen & 0xF) * 4; + debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src, &pkt->v4.dst); + src.type = mDNSAddrType_IPv4; src.ip.v4 = pkt->v4.src; + dst.type = mDNSAddrType_IPv4; dst.ip.v4 = pkt->v4.dst; + if (end >= trans + RequiredCapLen(pkt->v4.protocol)) + mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, end, InterfaceID, 0); + } + // Is IPv6? Length must be at least 14 + 28 = 42 bytes + else if (end >= p+54 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv6)) + { + const mDNSu8 *const trans = p + 54; + debugf("Got IPv6 %02X from %.16a to %.16a", pkt->v6.pro, &pkt->v6.src, &pkt->v6.dst); + src.type = mDNSAddrType_IPv6; src.ip.v6 = pkt->v6.src; + dst.type = mDNSAddrType_IPv6; dst.ip.v6 = pkt->v6.dst; + if (end >= trans + RequiredCapLen(pkt->v6.pro)) + mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v6.pro, p, (TransportLayerPacket*)trans, end, InterfaceID, + (mDNSu16)pkt->bytes[4] << 8 | pkt->bytes[5]); + } +} + +mDNSlocal void ConstructSleepProxyServerName(mDNS *const m, domainlabel *name) +{ + name->c[0] = (mDNSu8)mDNS_snprintf((char*)name->c+1, 62, "%d-%d-%d-%d.%d %#s", + m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags, &m->nicelabel); +} + +#ifndef SPC_DISABLED +mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) +{ + if (result == mStatus_NameConflict) + mDNS_RenameAndReregisterService(m, srs, mDNSNULL); + else if (result == mStatus_MemFree) + { + if (m->SleepState) + m->SPSState = 3; + else + { + m->SPSState = (mDNSu8)(m->SPSSocket != mDNSNULL); + if (m->SPSState) + { + domainlabel name; + ConstructSleepProxyServerName(m, &name); + mDNS_RegisterService(m, srs, + &name, &SleepProxyServiceType, &localdomain, + mDNSNULL, m->SPSSocket->port, // Host, port + (mDNSu8 *)"", 1, // TXT data, length + mDNSNULL, 0, // Subtypes (none) + mDNSInterface_Any, // Interface ID + SleepProxyServerCallback, mDNSNULL, 0); // Callback, context, flags + } + LogSPS("Sleep Proxy Server %#s %s", srs->RR_SRV.resrec.name->c, m->SPSState ? "started" : "stopped"); + } + } +} +#endif + +// Called with lock held +mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features) +{ + // This routine uses mDNS_DeregisterService and calls SleepProxyServerCallback, so we execute in user callback context + mDNS_DropLockBeforeCallback(); + + // If turning off SPS, close our socket + // (Do this first, BEFORE calling mDNS_DeregisterService below) + if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; } + + // If turning off, or changing type, deregister old name +#ifndef SPC_DISABLED + if (m->SPSState == 1 && sps != m->SPSType) + { m->SPSState = 2; mDNS_DeregisterService_drt(m, &m->SPSRecords, sps ? mDNS_Dereg_rapid : mDNS_Dereg_normal); } +#endif // SPC_DISABLED + + // Record our new SPS parameters + m->SPSType = sps; + m->SPSPortability = port; + m->SPSMarginalPower = marginalpower; + m->SPSTotalPower = totpower; + m->SPSFeatureFlags = features; + // If turning on, open socket and advertise service + if (sps) + { + if (!m->SPSSocket) + { + m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; } + } +#ifndef SPC_DISABLED + if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree); +#endif // SPC_DISABLED + } + else if (m->SPSState) + { + LogSPS("mDNSCoreBeSleepProxyServer turning off from state %d; will wake clients", m->SPSState); + m->NextScheduledSPS = m->timenow; + } +fail: + mDNS_ReclaimLockAfterCallback(); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Startup and Shutdown #endif mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) - { - if (storage && numrecords) - { - mDNSu32 i; - debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity)); - for (i=0; i<numrecords; i++) storage[i].next = &storage[i+1]; - storage[numrecords-1].next = m->rrcache_free; - m->rrcache_free = storage; - m->rrcache_size += numrecords; - } - } +{ + 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); - } +{ + 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; + CacheEntity *rrcachestorage, mDNSu32 rrcachesize, + mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) +{ + mDNSu32 slot; + mDNSs32 timenow; + mStatus result; + + if (!rrcachestorage) rrcachesize = 0; + + m->p = p; + m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise + m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; + m->DivertMulticastAdvertisements = mDNSfalse; + m->mDNSPlatformStatus = mStatus_Waiting; + m->UnicastPort4 = zeroIPPort; + m->UnicastPort6 = zeroIPPort; + m->PrimaryMAC = zeroEthAddr; + m->MainCallback = Callback; + m->MainContext = Context; + m->rec.r.resrec.RecordType = 0; + m->rec.r.resrec.AnonInfo = mDNSNULL; + + // For debugging: To catch and report locking failures + m->mDNS_busy = 0; + m->mDNS_reentrancy = 0; + m->ShutdownTime = 0; + m->lock_rrcache = 0; + m->lock_Questions = 0; + m->lock_Records = 0; + + // Task Scheduling variables + result = mDNSPlatformTimeInit(); + if (result != mStatus_NoError) return(result); + m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF); + timenow = mDNS_TimeNow_NoLock(m); + + m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section + m->timenow_last = timenow; + m->NextScheduledEvent = timenow; + m->SuppressSending = timenow; + m->NextCacheCheck = timenow + 0x78000000; + m->NextScheduledQuery = timenow + 0x78000000; + m->NextScheduledProbe = timenow + 0x78000000; + m->NextScheduledResponse = timenow + 0x78000000; + m->NextScheduledNATOp = timenow + 0x78000000; + m->NextScheduledSPS = timenow + 0x78000000; + m->NextScheduledKA = timenow + 0x78000000; + m->NextScheduledStopTime = timenow + 0x78000000; + m->RandomQueryDelay = 0; + m->RandomReconfirmDelay = 0; + m->PktNum = 0; + m->MPktNum = 0; + m->LocalRemoveEvents = mDNSfalse; + m->SleepState = SleepState_Awake; + m->SleepSeqNum = 0; + m->SystemWakeOnLANEnabled = mDNSfalse; + m->AnnounceOwner = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond); + m->DelaySleep = 0; + m->SleepLimit = 0; + +#if APPLE_OSX_mDNSResponder + m->StatStartTime = mDNSPlatformUTC(); + m->NextStatLogTime = m->StatStartTime + kDefaultNextStatsticsLogTime; + m->ActiveStatTime = 0; + m->UnicastPacketsSent = 0; + m->MulticastPacketsSent = 0; + m->RemoteSubnet = 0; +#endif // APPLE_OSX_mDNSResponder + + // These fields only required for mDNS Searcher... + m->Questions = mDNSNULL; + m->NewQuestions = mDNSNULL; + m->CurrentQuestion = mDNSNULL; + m->LocalOnlyQuestions = mDNSNULL; + m->NewLocalOnlyQuestions = mDNSNULL; + m->RestartQuestion = mDNSNULL; + m->ValidationQuestion = mDNSNULL; + m->rrcache_size = 0; + m->rrcache_totalused = 0; + m->rrcache_active = 0; + m->rrcache_report = 10; + m->rrcache_free = mDNSNULL; + + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + m->rrcache_hash[slot] = mDNSNULL; + m->rrcache_nextcheck[slot] = timenow + 0x78000000;; + } + + mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize); + m->rrauth.rrauth_free = mDNSNULL; + + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + m->rrauth.rrauth_hash[slot] = mDNSNULL; + + // Fields below only required for mDNS Responder... + m->hostlabel.c[0] = 0; + m->nicelabel.c[0] = 0; + m->MulticastHostname.c[0] = 0; + m->HIHardware.c[0] = 0; + m->HISoftware.c[0] = 0; + m->ResourceRecords = mDNSNULL; + m->DuplicateRecords = mDNSNULL; + m->NewLocalRecords = mDNSNULL; + m->NewLocalOnlyRecords = mDNSfalse; + m->CurrentRecord = mDNSNULL; + m->HostInterfaces = mDNSNULL; + m->ProbeFailTime = 0; + m->NumFailedProbes = 0; + m->SuppressProbes = 0; + +#ifndef UNICAST_DISABLED + m->NextuDNSEvent = timenow + 0x78000000; + m->NextSRVUpdate = timenow + 0x78000000; + + m->DNSServers = mDNSNULL; + + m->Router = zeroAddr; + m->AdvertisedV4 = zeroAddr; + m->AdvertisedV6 = zeroAddr; + + m->AuthInfoList = mDNSNULL; + + m->ReverseMap.ThisQInterval = -1; + m->StaticHostname.c[0] = 0; + m->FQDN.c[0] = 0; + m->Hostnames = mDNSNULL; + m->AutoTunnelNAT.clientContext = mDNSNULL; + + m->WABBrowseQueriesCount = 0; + m->WABLBrowseQueriesCount = 0; + m->WABRegQueriesCount = 0; +#if !TARGET_OS_EMBEDDED + m->mDNSOppCaching = mDNStrue; +#else + m->mDNSOppCaching = mDNSfalse; +#endif + m->AutoTargetServices = 0; + + // NAT traversal fields + m->LLQNAT.clientCallback = mDNSNULL; + m->LLQNAT.clientContext = mDNSNULL; + m->NATTraversals = mDNSNULL; + m->CurrentNATTraversal = mDNSNULL; + m->retryIntervalGetAddr = 0; // delta between time sent and retry + m->retryGetAddr = timenow + 0x78000000; // absolute time when we retry + m->ExtAddress = zerov4Addr; + m->PCPNonce[0] = mDNSRandom(-1); + m->PCPNonce[1] = mDNSRandom(-1); + m->PCPNonce[2] = mDNSRandom(-1); + + m->NATMcastRecvskt = mDNSNULL; + m->LastNATupseconds = 0; + m->LastNATReplyLocalTime = timenow; + m->LastNATMapResultCode = NATErr_None; + + m->UPnPInterfaceID = 0; + m->SSDPSocket = mDNSNULL; + m->SSDPWANPPPConnection = mDNSfalse; + m->UPnPRouterPort = zeroIPPort; + m->UPnPSOAPPort = zeroIPPort; + m->UPnPRouterURL = mDNSNULL; + m->UPnPWANPPPConnection = mDNSfalse; + m->UPnPSOAPURL = mDNSNULL; + m->UPnPRouterAddressString = mDNSNULL; + m->UPnPSOAPAddressString = mDNSNULL; + m->SPSType = 0; + m->SPSPortability = 0; + m->SPSMarginalPower = 0; + m->SPSTotalPower = 0; + m->SPSFeatureFlags = 0; + m->SPSState = 0; + m->SPSProxyListChanged = mDNSNULL; + m->SPSSocket = mDNSNULL; + m->SPSBrowseCallback = mDNSNULL; + m->ProxyRecords = 0; + #endif - result = mDNSPlatformInit(m); - return(result); - } +#if APPLE_OSX_mDNSResponder + m->TunnelClients = mDNSNULL; -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; +#if !NO_WCF + CHECK_WCF_FUNCTION(WCFConnectionNew) + { + m->WCF = WCFConnectionNew(); + if (!m->WCF) { LogMsg("WCFConnectionNew failed"); return -1; } + } +#endif -#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); - } + + result = mDNSPlatformInit(m); + +#ifndef UNICAST_DISABLED + // It's better to do this *after* the platform layer has set up the + // interface list and security credentials + uDNS_SetupDNSConfig(m); // Get initial DNS configuration +#endif + + return(result); +} + +mDNSexport void mDNS_ConfigChanged(mDNS *const m) +{ + if (m->SPSState == 1) + { + domainlabel name, newname; +#ifndef SPC_DISABLED + domainname type, domain; + DeconstructServiceName(m->SPSRecords.RR_SRV.resrec.name, &name, &type, &domain); +#endif // SPC_DISABLED + ConstructSleepProxyServerName(m, &newname); + if (!SameDomainLabelCS(name.c, newname.c)) + { + LogSPS("Renaming SPS from “%#s” to “%#s”", name.c, newname.c); + // When SleepProxyServerCallback gets the mStatus_MemFree message, + // it will reregister the service under the new name + m->SPSState = 2; +#ifndef SPC_DISABLED + mDNS_DeregisterService_drt(m, &m->SPSRecords, mDNS_Dereg_rapid); +#endif // SPC_DISABLED + } + } + + if (m->MainCallback) + m->MainCallback(m, mStatus_ConfigChanged); +} + +mDNSlocal void DynDNSHostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + (void)m; // unused + debugf("NameStatusCallback: result %d for registration of name %##s", result, rr->resrec.name->c); + mDNSPlatformDynDNSHostNameStatusChanged(rr->resrec.name, result); +} + +mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const DNSServer * const ptr, mDNSBool lameduck) +{ + mDNSBool purge = cr->resrec.RecordType == kDNSRecordTypePacketNegative || + cr->resrec.rrtype == kDNSType_A || + cr->resrec.rrtype == kDNSType_AAAA || + cr->resrec.rrtype == kDNSType_SRV; + + (void) lameduck; + (void) ptr; + debugf("PurgeOrReconfirmCacheRecord: %s cache record due to %s server %p %#a:%d (%##s): %s", + purge ? "purging" : "reconfirming", + lameduck ? "lame duck" : "new", + ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, CRDisplayString(m, cr)); + + if (purge) + { + LogInfo("PurgeorReconfirmCacheRecord: Purging Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType); + mDNS_PurgeCacheResourceRecord(m, cr); + } + else + { + LogInfo("PurgeorReconfirmCacheRecord: Reconfirming Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType); + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + } +} + +mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q) +{ + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + mDNSu8 validatingResponse = 0; + + // For DNSSEC questions, purge the corresponding RRSIGs also. + if (DNSSECQuestion(q)) + { + validatingResponse = q->ValidatingResponse; + q->ValidatingResponse = mDNStrue; + } + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (SameNameRecordAnswersQuestion(&rp->resrec, q)) + { + LogInfo("mDNS_PurgeForQuestion: Flushing %s", CRDisplayString(m, rp)); + mDNS_PurgeCacheResourceRecord(m, rp); + } + } + if (DNSSECQuestion(q)) + { + q->ValidatingResponse = validatingResponse; + } +} + +// For DNSSEC question, we need the DNSSEC records also. If the cache does not +// have the DNSSEC records, we need to re-issue the question with EDNS0/DO bit set. +// Just re-issuing the question for RRSIGs does not work in practice as the response +// may not contain the RRSIGs whose typeCovered field matches the question's qtype. +// +// For negative responses, we need the NSECs to prove the non-existence. If we don't +// have the cached NSECs, purge them. For positive responses, if we don't have the +// RRSIGs and if we have not already issued the question with EDNS0/DO bit set, purge +// them. +mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q) +{ + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (SameNameRecordAnswersQuestion(&rp->resrec, q)) + { + if (rp->resrec.RecordType != kDNSRecordTypePacketNegative || !rp->nsec) + { + if (!rp->CRDNSSECQuestion) + { + LogInfo("CheckForDNSSECRecords: Flushing %s", CRDisplayString(m, rp)); + mDNS_PurgeCacheResourceRecord(m, rp); + } + } + } + } +} + +// Check for a positive unicast response to the question but with qtype +mDNSexport mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype) +{ + DNSQuestion question; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + + // Create an identical question but with qtype + mDNS_SetupQuestion(&question, q->InterfaceID, &q->qname, qtype, mDNSNULL, mDNSNULL); + question.qDNSServer = q->qDNSServer; + + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (!rp->resrec.InterfaceID && rp->resrec.RecordType != kDNSRecordTypePacketNegative && + SameNameRecordAnswersQuestion(&rp->resrec, &question)) + { + LogInfo("mDNS_CheckForCacheRecord: Found %s", CRDisplayString(m, rp)); + return mDNStrue; + } + } + return mDNSfalse; +} + +mDNSexport void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *new) +{ + DNSQuestion *qptr; + + (void) m; + + if (q->DuplicateOf) + LogMsg("DNSServerChangeForQuestion: ERROR: Called for duplicate question %##s", q->qname.c); + + // Make sure all the duplicate questions point to the same DNSServer so that delivery + // of events for all of them are consistent. Duplicates for a question are always inserted + // after in the list. + q->qDNSServer = new; + for (qptr = q->next ; qptr; qptr = qptr->next) + { + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = new; } + } +} + +mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) +{ + McastResolver *mr; + DNSServer *ptr; + + if (delete) + { + for (ptr = m->DNSServers; ptr; ptr = ptr->next) + { + ptr->penaltyTime = 0; + NumUnicastDNSServers--; + ptr->flags |= DNSServer_FlagDelete; + } + // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at + // mcast resolvers. Today we get both mcast and ucast configuration using the same + // API + for (mr = m->McastResolvers; mr; mr = mr->next) + mr->flags |= McastResolver_FlagDelete; + } + else + { + for (ptr = m->DNSServers; ptr; ptr = ptr->next) + { + ptr->penaltyTime = 0; + NumUnicastDNSServers++; + ptr->flags &= ~DNSServer_FlagDelete; + } + for (mr = m->McastResolvers; mr; mr = mr->next) + mr->flags &= ~McastResolver_FlagDelete; + } +} + +mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + mDNSBool Restart = mDNSfalse; + mDNSAddr v4, v6, r; + domainname fqdn; + DNSServer *ptr, **p = &m->DNSServers; + const DNSServer *oldServers = m->DNSServers; + DNSQuestion *q; + McastResolver *mr, **mres = &m->McastResolvers; + + debugf("uDNS_SetupDNSConfig: entry"); + + // Let the platform layer get the current DNS information and setup the WAB queries if needed. + uDNS_SetupWABQueries(m); + + mDNS_Lock(m); + + // We need to first mark all the entries to be deleted. If the configuration changed, then + // the entries would be undeleted appropriately. Otherwise, we need to clear them. + // + // Note: The last argument to mDNSPlatformSetDNSConfig is "mDNStrue" which means ack the + // configuration. We already processed search domains in uDNS_SetupWABQueries above and + // hence we are ready to ack the configuration as this is the last call to mDNSPlatformSetConfig + // for the dns configuration change notification. + SetConfigState(m, mDNStrue); + if (!mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL, mDNStrue)) + { + SetConfigState(m, mDNSfalse); + mDNS_Unlock(m); + LogInfo("uDNS_SetupDNSConfig: No configuration change"); + return mStatus_NoError; + } + + // For now, we just delete the mcast resolvers. We don't deal with cache or + // questions here. Neither question nor cache point to mcast resolvers. Questions + // do inherit the timeout values from mcast resolvers. But we don't bother + // affecting them as they never change. + while (*mres) + { + if (((*mres)->flags & DNSServer_FlagDelete) != 0) + { + mr = *mres; + *mres = (*mres)->next; + debugf("uDNS_SetupDNSConfig: Deleting mcast resolver %##s", mr, mr->domain.c); + mDNSPlatformMemFree(mr); + } + else + { + (*mres)->flags &= ~McastResolver_FlagNew; + mres = &(*mres)->next; + } + } + + // Update our qDNSServer pointers before we go and free the DNSServer object memory + // + // All non-scoped resolvers share the same resGroupID. At no point in time a cache entry using DNSServer + // from scoped resolver will be used to answer non-scoped questions and vice versa, as scoped and non-scoped + // resolvers don't share the same resGroupID. A few examples to describe the interaction with how we pick + // DNSServers and flush the cache. + // + // - A non-scoped question picks DNSServer X, creates a cache entry with X. If a new resolver gets added later that + // is a better match, we pick the new DNSServer for the question and activate the unicast query. We may or may not + // flush the cache (See PurgeOrReconfirmCacheRecord). In either case, we don't change the cache record's DNSServer + // pointer immediately (qDNSServer and rDNSServer may be different but still share the same resGroupID). If we don't + // flush the cache immediately, the record's rDNSServer pointer will be updated (in mDNSCoreReceiveResponse) + // later when we get the response. If we purge the cache, we still deliver a RMV when it is purged even though + // we don't update the cache record's DNSServer pointer to match the question's DNSSever, as they both point to + // the same resGroupID. + // + // Note: If the new DNSServer comes back with a different response than what we have in the cache, we will deliver a RMV + // of the old followed by ADD of the new records. + // + // - A non-scoped question picks DNSServer X, creates a cache entry with X. If the resolver gets removed later, we will + // pick a new DNSServer for the question which may or may not be NULL and set the cache record's pointer to the same + // as in question's qDNSServer if the cache record is not flushed. If there is no active question, it will be set to NULL. + // + // - Two questions scoped and non-scoped for the same name will pick two different DNSServer and will end up creating separate + // cache records and as the resGroupID is different, you can't use the cache record from the scoped DNSServer to answer the + // non-scoped question and vice versa. + // + for (q = m->Questions; q; q=q->next) + { + if (!mDNSOpaque16IsZero(q->TargetQID)) + { + DNSServer *s, *t; + DNSQuestion *qptr; + if (q->DuplicateOf) continue; + SetValidDNSServers(m, q); + q->triedAllServersOnce = 0; + s = GetServerForQuestion(m, q); + t = q->qDNSServer; + if (t != s) + { + mDNSBool old, new; + mDNSIPPort tport, sport; + + if (t) + tport = t->port; else - m->CurrentRecord = rr->next; - } + tport = zeroIPPort; + if (s) + sport = s->port; + else + sport = zeroIPPort; + // If DNS Server for this question has changed, reactivate it + LogInfo("uDNS_SetupDNSConfig: Updating DNS Server from %#a:%d (%##s) to %#a:%d (%##s) for question %##s (%s) (scope:%p)", + t ? &t->addr : mDNSNULL, mDNSVal16(tport), t ? t->domain.c : (mDNSu8*)"", + s ? &s->addr : mDNSNULL, mDNSVal16(sport), s ? s->domain.c : (mDNSu8*)"", + q->qname.c, DNSTypeName(q->qtype), q->InterfaceID); + + old = q->SuppressQuery; + new = ShouldSuppressUnicastQuery(m, q, s); + if (old != new) + { + // Changing the DNS server affected the SuppressQuery status. We need to + // deliver RMVs for the previous ADDs (if any) before switching to the new + // DNSServer. To keep it simple, we walk all the questions and mark them + // to be restarted and then handle all of them at once. + q->Restart = 1; + q->SuppressQuery = new; + for (qptr = q->next ; qptr; qptr = qptr->next) + { + if (qptr->DuplicateOf == q) + qptr->Restart = 1; + } + Restart = mDNStrue; + } + else + { + DNSServerChangeForQuestion(m, q, s); + q->unansweredQueries = 0; + + // If we had sent a query out to DNSServer "t" and we are changing to "s", we + // need to ignore the responses coming back from "t" as the DNS configuration + // has changed e.g., when a new interface is coming up and that becomes the primary + // interface, we switch to the DNS servers configured for the primary interface. In + // this case, we should not accept responses associated with the previous interface as + // the "name" could resolve differently on this new primary interface. Hence, discard + // in-flight responses. + q->TargetQID = mDNS_NewMessageID(m); + + if (!QuerySuppressed(q)) + { + debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + ActivateUnicastQuery(m, q, mDNStrue); + // ActivateUnicastQuery is called for duplicate questions also as it does something + // special for AutoTunnel questions + for (qptr = q->next ; qptr; qptr = qptr->next) + { + if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue); + } + } + } + } + else + { + debugf("uDNS_SetupDNSConfig: Not Updating DNS server question %p %##s (%s) DNS server %#a:%d %p %d", + q, q->qname.c, DNSTypeName(q->qtype), t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), q->DuplicateOf, q->SuppressUnusable); + for (qptr = q->next ; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } + } + } + } + if (Restart) + RestartUnicastQuestions(m); + + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.InterfaceID) + continue; + + // We already walked the questions and restarted/reactivated them if the dns server + // change affected the question. That should take care of updating the cache. But + // what if there is no active question at this point when the DNS server change + // happened ? There could be old cache entries lying around and if we don't flush + // them, a new question after the DNS server change could pick up these stale + // entries and get a wrong answer. + // + // For cache entries that have active questions we might have skipped rescheduling + // the questions if they were suppressed (see above). To keep it simple, we walk + // all the cache entries to make sure that there are no stale entries. We use the + // active question's InterfaceID/ServiceID for looking up the right DNS server. + // Note that the unscoped value for ServiceID is -1. + // + // Note: If GetServerForName returns NULL, it could either mean that there are no + // DNS servers or no matching DNS servers for this question. In either case, + // the cache should get purged below when we process deleted DNS servers. + + ptr = GetServerForName(m, cr->resrec.name, + (cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL), + (cr->CRActiveQuestion ? cr->CRActiveQuestion->ServiceID : -1)); + + // Purge or Reconfirm if this cache entry would use the new DNS server + if (ptr && (ptr != cr->resrec.rDNSServer)) + { + // As the DNSServers for this cache record is not the same anymore, we don't + // want any new questions to pick this old value. If there is no active question, + // we can't possibly re-confirm, so purge in that case. If it is a DNSSEC question, + // purge the cache as the DNSSEC capabilities of the DNS server may have changed. + + if (cr->CRActiveQuestion == mDNSNULL || DNSSECQuestion(cr->CRActiveQuestion)) + { + LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s, New DNS server %#a , Old DNS server %#a", CRDisplayString(m, cr), + &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ? &cr->resrec.rDNSServer->addr : mDNSNULL)); + mDNS_PurgeCacheResourceRecord(m, cr); + } + else + { + LogInfo("uDNS_SetupDNSConfig: Purging/Reconfirming Resourcerecord %s, New DNS server %#a, Old DNS server %#a", CRDisplayString(m, cr), + &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ? &cr->resrec.rDNSServer->addr : mDNSNULL)); + PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); + } + } + } + + while (*p) + { + if (((*p)->flags & DNSServer_FlagDelete) != 0) + { + // Scan our cache, looking for uDNS records that we would have queried this server for. + // We reconfirm any records that match, because in this world of split DNS, firewalls, etc. + // different DNS servers can give different answers to the same question. + ptr = *p; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.InterfaceID) continue; + if (cr->resrec.rDNSServer == ptr) + { + // If we don't have an active question for this cache record, neither Purge can + // generate RMV events nor Reconfirm can send queries out. Just set the DNSServer + // pointer on the record NULL so that we don't point to freed memory (We might dereference + // DNSServer pointers from resource record for logging purposes). + // + // If there is an active question, point to its DNSServer as long as it does not point to the + // freed one. We already went through the questions above and made them point at either the + // new server or NULL if there is no server. + + if (cr->CRActiveQuestion) + { + DNSQuestion *qptr = cr->CRActiveQuestion; + + if (qptr->qDNSServer == ptr) + { + LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) poining to DNSServer Address %#a" + " to be freed", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), qptr->InterfaceID, &ptr->addr); + qptr->validDNSServers = zeroOpaque64; + qptr->qDNSServer = mDNSNULL; + cr->resrec.rDNSServer = mDNSNULL; + } + else + { + LogInfo("uDNS_SetupDNSConfig: Cache Record %s, Active question %##s (%s) (scope:%p), pointing to DNSServer %#a (to be deleted)," + " resetting to question's DNSServer Address %#a", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), + qptr->InterfaceID, &ptr->addr, (qptr->qDNSServer ? &qptr->qDNSServer->addr : mDNSNULL)); + cr->resrec.rDNSServer = qptr->qDNSServer; + } + } + else + { + LogInfo("uDNS_SetupDNSConfig: Cache Record %##s has no Active question, Record's DNSServer Address %#a, Server to be deleted %#a", + cr->resrec.name, &cr->resrec.rDNSServer->addr, &ptr->addr); + cr->resrec.rDNSServer = mDNSNULL; + } + + PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue); + } + } + *p = (*p)->next; + LogInfo("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s) %d", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, NumUnicastDNSServers); + mDNSPlatformMemFree(ptr); + } + else + { + (*p)->flags &= ~DNSServer_FlagNew; + p = &(*p)->next; + } + } + + // If we now have no DNS servers at all and we used to have some, then immediately purge all unicast cache records (including for LLQs). + // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless. + // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour. + // Similarly, if we now have some DNS servers and we used to have none, we want to purge any fake negative results we may have generated. + if ((m->DNSServers != mDNSNULL) != (oldServers != mDNSNULL)) + { + int count = 0; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (!cr->resrec.InterfaceID) + { + mDNS_PurgeCacheResourceRecord(m, cr); + count++; + } + } + LogInfo("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache", + m->DNSServers ? "DNS server became" : "No DNS servers", count); + + // Force anything that needs to get zone data to get that information again + RestartRecordGetZoneData(m); + } + + // Did our FQDN change? + if (!SameDomainName(&fqdn, &m->FQDN)) + { + if (m->FQDN.c[0]) mDNS_RemoveDynDNSHostName(m, &m->FQDN); + + AssignDomainName(&m->FQDN, &fqdn); + + if (m->FQDN.c[0]) + { + mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); + mDNS_AddDynDNSHostName(m, &m->FQDN, DynDNSHostNameCallback, mDNSNULL); + } + } + + mDNS_Unlock(m); + + // handle router and primary interface changes + v4 = v6 = r = zeroAddr; + v4.type = r.type = mDNSAddrType_IPv4; + + if (mDNSPlatformGetPrimaryInterface(m, &v4, &v6, &r) == mStatus_NoError && !mDNSv4AddressIsLinkLocal(&v4.ip.v4)) + { + mDNS_SetPrimaryInterfaceInfo(m, + !mDNSIPv4AddressIsZero(v4.ip.v4) ? &v4 : mDNSNULL, + !mDNSIPv6AddressIsZero(v6.ip.v6) ? &v6 : mDNSNULL, + !mDNSIPv4AddressIsZero(r.ip.v4) ? &r : mDNSNULL); + } + else + { + mDNS_SetPrimaryInterfaceInfo(m, mDNSNULL, mDNSNULL, mDNSNULL); + if (m->FQDN.c[0]) mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); // Set status to 1 to indicate temporary failure + } + + debugf("uDNS_SetupDNSConfig: number of unicast DNS servers %d", NumUnicastDNSServers); + return mStatus_NoError; +} - if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records"); - else debugf("mDNS_Close: No deregistering records remain"); +mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) +{ + m->mDNSPlatformStatus = result; + if (m->MainCallback) + { + mDNS_Lock(m); + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + m->MainCallback(m, mStatus_NoError); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + mDNS_Unlock(m); + } +} + +mDNSlocal void DeregLoop(mDNS *const m, AuthRecord *const start) +{ + m->CurrentRecord = start; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + LogInfo("DeregLoop: %s deregistration for %p %02X %s", + (rr->resrec.RecordType != kDNSRecordTypeDeregistering) ? "Initiating " : "Accelerating", + rr, rr->resrec.RecordType, ARDisplayString(m, rr)); + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) + mDNS_Deregister_internal(m, rr, mDNS_Dereg_rapid); + else if (rr->AnnounceCount > 1) + { + rr->AnnounceCount = 1; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} + +mDNSexport void mDNS_StartExit(mDNS *const m) +{ + NetworkInterfaceInfo *intf; + AuthRecord *rr; + + mDNS_Lock(m); + + LogInfo("mDNS_StartExit"); + m->ShutdownTime = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); + + mDNSCoreBeSleepProxyServer_internal(m, 0, 0, 0, 0, 0); + +#if APPLE_OSX_mDNSResponder +#if !NO_WCF + CHECK_WCF_FUNCTION(WCFConnectionDealloc) + { + if (m->WCF) WCFConnectionDealloc((WCFConnection *)m->WCF); + } +#endif +#endif - // 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"); - } +#ifndef UNICAST_DISABLED + { + SearchListElem *s; + SuspendLLQs(m); + // Don't need to do SleepRecordRegistrations() here + // because we deregister all records and services later in this routine + while (m->Hostnames) mDNS_RemoveDynDNSHostName(m, &m->Hostnames->fqdn); + + // For each member of our SearchList, deregister any records it may have created, and cut them from the list. + // Otherwise they'll be forcibly deregistered for us (without being cut them from the appropriate list) + // and we may crash because the list still contains dangling pointers. + for (s = SearchList; s; s = s->next) + while (s->AuthRecs) + { + ARListElem *dereg = s->AuthRecs; + s->AuthRecs = s->AuthRecs->next; + mDNS_Deregister_internal(m, &dereg->ar, mDNS_Dereg_normal); // Memory will be freed in the FreeARElemCallback + } + } +#endif + + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) + DeadvertiseInterface(m, intf); + + // Shut down all our active NAT Traversals + while (m->NATTraversals) + { + NATTraversalInfo *t = m->NATTraversals; + mDNS_StopNATOperation_internal(m, t); // This will cut 't' from the list, thereby advancing m->NATTraversals in the process + + // After stopping the NAT Traversal, we zero out the fields. + // This has particularly important implications for our AutoTunnel records -- + // when we deregister our AutoTunnel records below, we don't want their mStatus_MemFree + // handlers to just turn around and attempt to re-register those same records. + // Clearing t->ExternalPort/t->RequestedPort will cause the mStatus_MemFree callback handlers + // to not do this. + t->ExternalAddress = zerov4Addr; + t->NewAddress = zerov4Addr; + t->ExternalPort = zeroIPPort; + t->RequestedPort = zeroIPPort; + t->Lifetime = 0; + t->Result = mStatus_NoError; + } + + // Make sure there are nothing but deregistering records remaining in the list + if (m->CurrentRecord) + LogMsg("mDNS_StartExit: ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + + // We're in the process of shutting down, so queries, etc. are no longer available. + // Consequently, determining certain information, e.g. the uDNS update server's IP + // address, will not be possible. The records on the main list are more likely to + // already contain such information, so we deregister the duplicate records first. + LogInfo("mDNS_StartExit: Deregistering duplicate resource records"); + DeregLoop(m, m->DuplicateRecords); + LogInfo("mDNS_StartExit: Deregistering resource records"); + DeregLoop(m, m->ResourceRecords); + + // If we scheduled a response to send goodbye packets, we set NextScheduledResponse to now. Normally when deregistering records, + // we allow up to 100ms delay (to help improve record grouping) but when shutting down we don't want any such delay. + if (m->NextScheduledResponse - m->timenow < mDNSPlatformOneSecond) + { + m->NextScheduledResponse = m->timenow; + m->SuppressSending = 0; + } + + if (m->ResourceRecords) LogInfo("mDNS_StartExit: Sending final record deregistrations"); + else LogInfo("mDNS_StartExit: No deregistering records remain"); + + for (rr = m->DuplicateRecords; rr; rr = rr->next) + LogMsg("mDNS_StartExit: Should not still have Duplicate Records remaining: %02X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); + + // If any deregistering records remain, send their deregistration announcements before we exit + if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m); + + mDNS_Unlock(m); + + LogInfo("mDNS_StartExit: done"); +} + +mDNSexport void mDNS_FinalExit(mDNS *const m) +{ + mDNSu32 rrcache_active = 0; + mDNSu32 slot; + AuthRecord *rr; + + LogInfo("mDNS_FinalExit: mDNSPlatformClose"); + mDNSPlatformClose(m); + + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + while (m->rrcache_hash[slot]) + { + CacheGroup *cg = m->rrcache_hash[slot]; + while (cg->members) + { + CacheRecord *cr = cg->members; + cg->members = cg->members->next; + if (cr->CRActiveQuestion) rrcache_active++; + ReleaseCacheRecord(m, cr); + } + cg->rrcache_tail = &cg->members; + ReleaseCacheGroup(m, &m->rrcache_hash[slot]); + } + } + debugf("mDNS_FinalExit: RR Cache was using %ld records, %lu active", m->rrcache_totalused, rrcache_active); + if (rrcache_active != m->rrcache_active) + LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); + + for (rr = m->ResourceRecords; rr; rr = rr->next) + LogMsg("mDNS_FinalExit failed to send goodbye for: %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr)); + + LogInfo("mDNS_FinalExit: done"); +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c index 656b36b774..cb4da6ecef 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c @@ -5,70 +5,43 @@ * 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) + File: mDNSDebug.c -Revision 1.2 2003/12/09 01:30:40 rpantos -Fix usage of ARGS... macros to build properly on Windows. + Contains: Implementation of debugging utilities. Requires a POSIX environment. -Revision 1.1 2003/12/08 21:11:42; rpantos -Changes necessary to support mDNSResponder on Linux. + Version: 1.0 -*/ - -#pragma ident "%Z%%M% %I% %E% SMI" + */ #include "mDNSDebug.h" #include <stdio.h> -#if defined(WIN32) -// Need to add Windows syslog support here +#if defined(WIN32) || defined(EFI32) || defined(EFI64) || defined(EFIX64) +// Need to add Windows/EFI syslog support here #define LOG_PID 0x01 #define LOG_CONS 0x02 #define LOG_PERROR 0x20 -#define openlog(A,B,C) (void)(A); (void)(B) -#define syslog(A,B,C) -#define closelog() #else #include <syslog.h> #endif #include "mDNSEmbeddedAPI.h" +mDNSexport int mDNS_LoggingEnabled = 0; +mDNSexport int mDNS_PacketLoggingEnabled = 0; +mDNSexport int mDNS_McastLoggingEnabled = 0; +mDNSexport int mDNS_McastTracingEnabled = 0; + #if MDNS_DEBUGMSGS mDNSexport int mDNS_DebugMode = mDNStrue; #else @@ -77,76 +50,46 @@ mDNSexport int mDNS_DebugMode = mDNSfalse; // 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); - } +{ + char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf(buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + mDNSPlatformWriteDebugMsg(buffer); +} #endif -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 +mDNSlocal void LogMsgWithLevelv(mDNSLogLevel_t logLevel, const char *format, va_list ptr) +{ + char buffer[512]; + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + mDNSPlatformWriteLogMsg(ProgramName, buffer, logLevel); +} + +#define LOG_HELPER_BODY(L) \ + { \ + va_list ptr; \ + va_start(ptr,format); \ + LogMsgWithLevelv(L, format, ptr); \ + va_end(ptr); \ + } + +// see mDNSDebug.h +#if !MDNS_HAS_VA_ARG_MACROS +void LogMsg_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_MSG) +void LogOperation_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_OPERATION) +void LogSPS_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_SPS) +void LogInfo_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_INFO) +#endif + +#if MDNS_DEBUGMSGS +void debugf_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_DEBUG) +#endif // Log message with default "mDNSResponder" ident string at the start -mDNSexport void 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); - } +mDNSexport void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...) +LOG_HELPER_BODY(logLevel) diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h index 18be95a2b5..8bf4e5ada8 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h @@ -5,84 +5,15 @@ * 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 @@ -104,89 +35,132 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release // warning: repeated `#' flag in format (for %##s -- DNS name string format) // warning: double format, pointer arg (arg 2) (for %.4a, %.16a, %#a -- IP address formats) #define MDNS_CHECK_PRINTF_STYLE_FUNCTIONS 0 + +typedef enum +{ + MDNS_LOG_MSG, + MDNS_LOG_OPERATION, + MDNS_LOG_SPS, + MDNS_LOG_INFO, + MDNS_LOG_DEBUG, +} mDNSLogLevel_t; + +// Set this symbol to 1 to answer remote queries for our Address, reverse mapping PTR, and HINFO records +#define ANSWER_REMOTE_HOSTNAME_QUERIES 0 + +// Set this symbol to 1 to do extra debug checks on malloc() and free() +// Set this symbol to 2 to write a log message for every malloc() and free() +//#define MACOSX_MDNS_MALLOC_DEBUGGING 1 + +//#define ForceAlerts 1 +//#define LogTimeStamps 1 + +// Developer-settings section ends here + #if MDNS_CHECK_PRINTF_STYLE_FUNCTIONS #define IS_A_PRINTF_STYLE_FUNCTION(F,A) __attribute__ ((format(printf,F,A))) #else #define IS_A_PRINTF_STYLE_FUNCTION(F,A) #endif -#ifdef __cplusplus - extern "C" { +#ifdef __cplusplus +extern "C" { +#endif + +// Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing. + +#if (defined(__GNUC__)) + #if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 2))) + #define MDNS_C99_VA_ARGS 1 + #define MDNS_GNU_VA_ARGS 0 + #else + #define MDNS_C99_VA_ARGS 0 + #define MDNS_GNU_VA_ARGS 1 + #endif + #define MDNS_HAS_VA_ARG_MACROS 1 +#elif (_MSC_VER >= 1400) // Visual Studio 2005 and later + #define MDNS_C99_VA_ARGS 1 + #define MDNS_GNU_VA_ARGS 0 + #define MDNS_HAS_VA_ARG_MACROS 1 +#elif (defined(__MWERKS__)) + #define MDNS_C99_VA_ARGS 1 + #define MDNS_GNU_VA_ARGS 0 + #define MDNS_HAS_VA_ARG_MACROS 1 +#else + #define MDNS_C99_VA_ARGS 1 + #define MDNS_GNU_VA_ARGS 0 + #define MDNS_HAS_VA_ARG_MACROS 1 +#endif + +#if (MDNS_HAS_VA_ARG_MACROS) + #if (MDNS_C99_VA_ARGS) + #define debug_noop(... ) ((void)0) + #define LogMsg(... ) LogMsgWithLevel(MDNS_LOG_MSG, __VA_ARGS__) + #define LogOperation(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__);} while (0) + #define LogSPS(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, __VA_ARGS__);} while (0) + #define LogInfo(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, __VA_ARGS__);} while (0) + #elif (MDNS_GNU_VA_ARGS) + #define debug_noop( ARGS... ) ((void)0) + #define LogMsg( ARGS... ) LogMsgWithLevel(MDNS_LOG_MSG, ARGS) + #define LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS);} while (0) + #define LogSPS( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, ARGS);} while (0) + #define LogInfo( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, ARGS);} while (0) + #else + #error Unknown variadic macros + #endif +#else +// If your platform does not support variadic macros, you need to define the following variadic functions. +// See mDNSShared/mDNSDebug.c for sample implementation + #define debug_noop 1 ? (void)0 : (void) + #define LogMsg LogMsg_ + #define LogOperation (mDNS_LoggingEnabled == 0) ? ((void)0) : LogOperation_ + #define LogSPS (mDNS_LoggingEnabled == 0) ? ((void)0) : LogSPS_ + #define LogInfo (mDNS_LoggingEnabled == 0) ? ((void)0) : LogInfo_ +extern void LogMsg_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogOperation_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogSPS_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogInfo_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #endif #if MDNS_DEBUGMSGS #define debugf debugf_ extern void debugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); -#else // 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 +#else +#define debugf debug_noop #endif #if MDNS_DEBUGMSGS > 1 #define verbosedebugf verbosedebugf_ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #else - #if (defined(__GNUC__)) - #define verbosedebugf( ARGS... ) ((void)0) - #elif (defined(__MWERKS__)) - #define verbosedebugf( ... ) - #else - #define verbosedebugf 1 ? ((void)0) : (void) - #endif +#define verbosedebugf debug_noop #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); +extern int mDNS_LoggingEnabled; +extern int mDNS_PacketLoggingEnabled; +extern int mDNS_McastLoggingEnabled; +extern int mDNS_McastTracingEnabled; +extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog +extern const char ProgramName[]; -// Set this symbol to 1 to answer remote queries for our Address, reverse mapping PTR, and HINFO records -#define ANSWER_REMOTE_HOSTNAME_QUERIES 0 +extern void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(2,3); +// LogMsgNoIdent needs to be fixed so that it logs without the ident prefix like it used to +// (or completely overhauled to use the new "log to a separate file" facility) +#define LogMsgNoIdent LogMsg -// 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 +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 extern void *mallocL(char *msg, unsigned int size); extern void freeL(char *msg, void *x); extern void LogMemCorruption(const char *format, ...); extern void uds_validatelists(void); +extern void udns_validatelists(void *const v); #else #define mallocL(X,Y) malloc(Y) #define freeL(X,Y) free(Y) #endif -#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 - } +#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 index 5ed2c92240..75e6fc0991 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h @@ -1,20 +1,19 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 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. @@ -33,13 +32,13 @@ 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 @@ -50,998 +49,61 @@ 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 +#ifndef __mDNSEmbeddedAPI_h +#define __mDNSEmbeddedAPI_h -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 +#if defined(EFI32) || defined(EFI64) || defined(EFIX64) +// EFI doesn't have stdarg.h unless it's building with GCC. +#include "Tiano.h" +#if !defined(__GNUC__) +#define va_list VA_LIST +#define va_start(a, b) VA_START(a, b) +#define va_end(a) VA_END(a) +#define va_arg(a, b) VA_ARG(a, b) +#endif +#else +#include <stdarg.h> // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration +#endif -Revision 1.22 2002/09/19 04:20:43 cheshire -Remove high-ascii characters that confuse some systems +#include "mDNSDebug.h" +#if APPLE_OSX_mDNSResponder +#include <uuid/uuid.h> +#endif -Revision 1.21 2002/09/17 01:06:35 cheshire -Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() +#ifdef __cplusplus +extern "C" { +#endif -Revision 1.20 2002/09/16 18:41:41 cheshire -Merge in license terms from Quinn's copy, in preparation for Darwin release +// *************************************************************************** +// Feature removal compile options & limited resource targets -*/ +// The following compile options are responsible for removing certain features from mDNSCore to reduce the +// memory footprint for use in embedded systems with limited resources. -#pragma ident "%Z%%M% %I% %E% SMI" +// UNICAST_DISABLED - disables unicast DNS functionality, including Wide Area Bonjour +// ANONYMOUS_DISABLED - disables anonymous functionality +// DNSSEC_DISABLED - disables DNSSEC functionality +// SPC_DISABLED - disables Bonjour Sleep Proxy client +// IDLESLEEPCONTROL_DISABLED - disables sleep control for Bonjour Sleep Proxy clients -#ifndef __mDNSClientAPI_h -#define __mDNSClientAPI_h +// In order to disable the above features pass the option to your compiler, e.g. -D UNICAST_DISABLED -#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 +// Additionally, the LIMITED_RESOURCES_TARGET compile option will eliminate caching and +// and reduce the maximum DNS message sizes. -#include "mDNSDebug.h" +#ifdef LIMITED_RESOURCES_TARGET +// Don't support jumbo frames +#define AbsoluteMaxDNSMessageData 1500 +// By the time you add IPv6 header (40 bytes) UDP header (8 bytes) and DNS message header (12 bytes) +// this makes 1560 which is 60 bytes over the standard Ethernet MTU. D'oh! -#ifdef __cplusplus - extern "C" { +// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes) +#define MaximumRDSize 264 +// Don't cache anything +#define AUTH_HASH_SLOTS 1 +#define CACHE_HASH_SLOTS 1 #endif // *************************************************************************** @@ -1094,80 +156,115 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release #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; +typedef enum // From RFC 1035 +{ + kDNSClass_IN = 1, // Internet + kDNSClass_CS = 2, // CSNET + kDNSClass_CH = 3, // CHAOS + kDNSClass_HS = 4, // Hesiod + kDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] + + kDNSClass_Mask = 0x7FFF, // Multicast DNS uses the bottom 15 bits to identify the record class... + kDNSClass_UniqueRRSet = 0x8000, // ... and the top bit indicates that all other cached records are now invalid + + kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" + kDNSQClass_UnicastResponse = 0x8000 // Top bit set in a question means "unicast response acceptable" +} DNS_ClassValues; + +typedef enum // From RFC 1035 +{ + kDNSType_A = 1, // 1 Address + kDNSType_NS, // 2 Name Server + kDNSType_MD, // 3 Mail Destination + kDNSType_MF, // 4 Mail Forwarder + kDNSType_CNAME, // 5 Canonical Name + kDNSType_SOA, // 6 Start of Authority + kDNSType_MB, // 7 Mailbox + kDNSType_MG, // 8 Mail Group + kDNSType_MR, // 9 Mail Rename + kDNSType_NULL, // 10 NULL RR + kDNSType_WKS, // 11 Well-known-service + kDNSType_PTR, // 12 Domain name pointer + kDNSType_HINFO, // 13 Host information + kDNSType_MINFO, // 14 Mailbox information + kDNSType_MX, // 15 Mail Exchanger + kDNSType_TXT, // 16 Arbitrary text string + kDNSType_RP, // 17 Responsible person + kDNSType_AFSDB, // 18 AFS cell database + kDNSType_X25, // 19 X_25 calling address + kDNSType_ISDN, // 20 ISDN calling address + kDNSType_RT, // 21 Router + kDNSType_NSAP, // 22 NSAP address + kDNSType_NSAP_PTR, // 23 Reverse NSAP lookup (deprecated) + kDNSType_SIG, // 24 Security signature + kDNSType_KEY, // 25 Security key + kDNSType_PX, // 26 X.400 mail mapping + kDNSType_GPOS, // 27 Geographical position (withdrawn) + kDNSType_AAAA, // 28 IPv6 Address + kDNSType_LOC, // 29 Location Information + kDNSType_NXT, // 30 Next domain (security) + kDNSType_EID, // 31 Endpoint identifier + kDNSType_NIMLOC, // 32 Nimrod Locator + kDNSType_SRV, // 33 Service record + kDNSType_ATMA, // 34 ATM Address + kDNSType_NAPTR, // 35 Naming Authority PoinTeR + kDNSType_KX, // 36 Key Exchange + kDNSType_CERT, // 37 Certification record + kDNSType_A6, // 38 IPv6 Address (deprecated) + kDNSType_DNAME, // 39 Non-terminal DNAME (for IPv6) + kDNSType_SINK, // 40 Kitchen sink (experimental) + kDNSType_OPT, // 41 EDNS0 option (meta-RR) + kDNSType_APL, // 42 Address Prefix List + kDNSType_DS, // 43 Delegation Signer + kDNSType_SSHFP, // 44 SSH Key Fingerprint + kDNSType_IPSECKEY, // 45 IPSECKEY + kDNSType_RRSIG, // 46 RRSIG + kDNSType_NSEC, // 47 Denial of Existence + kDNSType_DNSKEY, // 48 DNSKEY + kDNSType_DHCID, // 49 DHCP Client Identifier + kDNSType_NSEC3, // 50 Hashed Authenticated Denial of Existence + kDNSType_NSEC3PARAM, // 51 Hashed Authenticated Denial of Existence + + kDNSType_HIP = 55, // 55 Host Identity Protocol + + kDNSType_SPF = 99, // 99 Sender Policy Framework for E-Mail + kDNSType_UINFO, // 100 IANA-Reserved + kDNSType_UID, // 101 IANA-Reserved + kDNSType_GID, // 102 IANA-Reserved + kDNSType_UNSPEC, // 103 IANA-Reserved + + kDNSType_TKEY = 249, // 249 Transaction key + kDNSType_TSIG, // 250 Transaction signature + kDNSType_IXFR, // 251 Incremental zone transfer + kDNSType_AXFR, // 252 Transfer zone of authority + kDNSType_MAILB, // 253 Transfer mailbox records + kDNSType_MAILA, // 254 Transfer mail agent records + kDNSQType_ANY // Not a DNS type, but a DNS query type, meaning "all types" +} DNS_TypeValues; // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Simple types #endif // mDNS defines its own names for these common types to simplify portability across // multiple platforms that may each have their own (different) names for these types. -typedef int mDNSBool; -typedef signed char mDNSs8; -typedef unsigned char mDNSu8; +typedef unsigned char 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() +// Source: http://www.unix.org/version2/whatsnew/lp64_wp.html +// http://software.intel.com/sites/products/documentation/hpc/mkl/lin/MKL_UG_structure/Support_for_ILP64_Programming.htm +// It can be safely assumed that int is 32bits on the platform #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; +typedef signed int mDNSs32; +typedef unsigned int mDNSu32; #endif // To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct @@ -1182,128 +279,210 @@ typedef struct mDNSInterfaceID_dummystruct { void *dummy; } *mDNSInterfaceID; // 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; +#if defined(_WIN32) + #pragma pack(push,2) +#elif !defined(__GNUC__) + #pragma pack(1) +#endif +typedef union { mDNSu8 b[ 2]; mDNSu16 NotAnInteger; } mDNSOpaque16; +typedef union { mDNSu8 b[ 4]; mDNSu32 NotAnInteger; } mDNSOpaque32; typedef packedunion { mDNSu8 b[ 6]; mDNSu16 w[3]; mDNSu32 l[1]; } mDNSOpaque48; -typedef packedunion { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128; +typedef union { mDNSu8 b[ 8]; mDNSu16 w[4]; mDNSu32 l[2]; } mDNSOpaque64; +typedef union { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128; +#if defined(_WIN32) + #pragma pack(pop) +#elif !defined(__GNUC__) + #pragma pack() +#endif -typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) -typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identifier (not an integer) -typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) -typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque identifier (not an integer) +typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) +typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identifier (not an integer) +typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) +typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque identifier (not an integer) + +// Bit operations for opaque 64 bit quantity. Uses the 32 bit quantity(l[2]) to set and clear bits +#define mDNSNBBY 8 +#define bit_set_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] |= (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) +#define bit_clr_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] &= ~(1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) +#define bit_get_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] & (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) + +enum +{ + mDNSAddrType_None = 0, + mDNSAddrType_IPv4 = 4, + mDNSAddrType_IPv6 = 6, + mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording +}; enum - { - mDNSAddrType_None = 0, - mDNSAddrType_IPv4 = 4, - mDNSAddrType_IPv6 = 6, - mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording - }; +{ + mDNSTransport_None = 0, + mDNSTransport_UDP = 1, + mDNSTransport_TCP = 2 +}; typedef struct - { - mDNSs32 type; - union { mDNSv6Addr v6; mDNSv4Addr v4; } ip; - } mDNSAddr; +{ + 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 - }; +{ + mStatus_Waiting = 1, + mStatus_NoError = 0, + + // mDNS return values are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537) + // The top end of the range (FFFE FFFF) is used for error codes; + // the bottom end of the range (FFFE FF00) is used for non-error values; + + // Error codes: + mStatus_UnknownErr = -65537, // First value: 0xFFFE FFFF + mStatus_NoSuchNameErr = -65538, + mStatus_NoMemoryErr = -65539, + mStatus_BadParamErr = -65540, + mStatus_BadReferenceErr = -65541, + mStatus_BadStateErr = -65542, + mStatus_BadFlagsErr = -65543, + mStatus_UnsupportedErr = -65544, + mStatus_NotInitializedErr = -65545, + mStatus_NoCache = -65546, + mStatus_AlreadyRegistered = -65547, + mStatus_NameConflict = -65548, + mStatus_Invalid = -65549, + mStatus_Firewall = -65550, + mStatus_Incompatible = -65551, + mStatus_BadInterfaceErr = -65552, + mStatus_Refused = -65553, + mStatus_NoSuchRecord = -65554, + mStatus_NoAuth = -65555, + mStatus_NoSuchKey = -65556, + mStatus_NATTraversal = -65557, + mStatus_DoubleNAT = -65558, + mStatus_BadTime = -65559, + mStatus_BadSig = -65560, // while we define this per RFC 2845, BIND 9 returns Refused for bad/missing signatures + mStatus_BadKey = -65561, + mStatus_TransientErr = -65562, // transient failures, e.g. sending packets shortly after a network transition or wake from sleep + mStatus_ServiceNotRunning = -65563, // Background daemon not running + mStatus_NATPortMappingUnsupported = -65564, // NAT doesn't support PCP, NAT-PMP or UPnP + mStatus_NATPortMappingDisabled = -65565, // NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator + mStatus_NoRouter = -65566, + mStatus_PollingMode = -65567, + mStatus_Timeout = -65568, + // -65568 to -65786 currently unused; available for allocation + + // tcp connection status + mStatus_ConnPending = -65787, + mStatus_ConnFailed = -65788, + mStatus_ConnEstablished = -65789, + + // Non-error values: + mStatus_GrowCache = -65790, + mStatus_ConfigChanged = -65791, + mStatus_MemFree = -65792 // Last value: 0xFFFE FF00 + // mStatus_MemFree is the last legal mDNS error code, at the end of the range allocated for mDNS +}; typedef mDNSs32 mStatus; +#define MaxIp 5 // Needs to be consistent with MaxInputIf in dns_services.h + +typedef enum { q_stop = 0, q_start } q_state; +typedef enum { reg_stop = 0, reg_start } reg_state; // 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 +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 +// RFC 1034/1035/2181 specify that a domain name (length bytes and data bytes) may be up to 255 bytes long, +// plus the terminating zero at the end makes 256 bytes total in the on-the-wire format. +#define MAX_DOMAIN_NAME 256 +typedef struct { mDNSu8 c[256]; } domainname; // Up to 256 bytes of length-prefixed domainlabels -typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string +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. +// The longest legal textual form of a DNS name is 1009 bytes, including the C-string terminating NULL at the end. // Explanation: // When a native domainname object is converted to printable textual form using ConvertDomainNameToCString(), // non-printing characters are represented in the conventional DNS way, as '\ddd', where ddd is a three-digit decimal number. -// The longest legal domain name is 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. +// The longest legal domain name is 256 bytes, in the form of four labels as shown below: +// Length byte, 63 data bytes, length byte, 63 data bytes, length byte, 63 data bytes, length byte, 62 data bytes, zero byte. // Each label is encoded textually as characters followed by a trailing dot. // If every character has to be represented as a four-byte escape sequence, then this makes the maximum textual form four labels // plus the C-string terminating NULL as shown below: -// 63*4+1 + 63*4+1 + 63*4+1 + 61*4+1 + 1 = 1005. +// 63*4+1 + 63*4+1 + 63*4+1 + 62*4+1 + 1 = 1009. // Note that MAX_ESCAPED_DOMAIN_LABEL is not normally used: If you're only decoding a single label, escaping is usually not required. // It is for domain names, where dots are used as label separators, that proper escaping is vital. #define MAX_ESCAPED_DOMAIN_LABEL 254 -#define MAX_ESCAPED_DOMAIN_NAME 1005 +#define MAX_ESCAPED_DOMAIN_NAME 1009 + +// MAX_REVERSE_MAPPING_NAME +// For IPv4: "123.123.123.123.in-addr.arpa." 30 bytes including terminating NUL +// For IPv6: "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa." 74 bytes including terminating NUL + +#define MAX_REVERSE_MAPPING_NAME_V4 30 +#define MAX_REVERSE_MAPPING_NAME_V6 74 +#define MAX_REVERSE_MAPPING_NAME 74 // Most records have a TTL of 75 minutes, so that their 80% cache-renewal query occurs once per hour. // For records containing a hostname (in the name on the left, or in the rdata on the right), // like A, AAAA, reverse-mapping PTR, and SRV, we use a two-minute TTL by default, because we don't want // them to hang around for too long in the cache if the host in question crashes or otherwise goes away. -// 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 + +// Some applications want to register their SRV records with a lower ttl so that in case the server +// using a dynamic port number restarts, the clients will not have stale information for more than +// 10 seconds + +#define kHostNameSmallTTL 10UL + + +// Multicast DNS uses announcements (gratuitous responses) to update peer caches. +// This means it is feasible to use relatively larger TTL values than we might otherwise +// use, because we have a cache coherency protocol to keep the peer caches up to date. +// With Unicast DNS, once an authoritative server gives a record with a certain TTL value to a client +// or caching server, that client or caching server is entitled to hold onto the record until its TTL +// expires, and has no obligation to contact the authoritative server again until that time arrives. +// This means that whereas Multicast DNS can use announcements to pre-emptively update stale data +// before it would otherwise have expired, standard Unicast DNS (not using LLQs) has no equivalent +// mechanism, and TTL expiry is the *only* mechanism by which stale data gets deleted. Because of this, +// we currently limit the TTL to ten seconds in such cases where no dynamic cache updating is possible. +#define kStaticCacheTTL 10 #define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL) +#define mDNS_KeepaliveRecord(rr) ((rr)->rrtype == kDNSType_NULL && SameDomainLabel(SecondLabel((rr)->name)->c, (mDNSu8 *)"\x0A_keepalive")) + +// Number of times keepalives are sent if no ACK is received before waking up the system +// this is analogous to net.inet.tcp.keepcnt +#define kKeepaliveRetryCount 10 +// The frequency at which keepalives are retried if no ACK is received +#define kKeepaliveRetryInterval 30 + +typedef struct AuthRecord_struct AuthRecord; +typedef struct ServiceRecordSet_struct ServiceRecordSet; +typedef struct CacheRecord_struct CacheRecord; +typedef struct CacheGroup_struct CacheGroup; +typedef struct AuthGroup_struct AuthGroup; +typedef struct DNSQuestion_struct DNSQuestion; +typedef struct ZoneData_struct ZoneData; +typedef struct mDNS_struct mDNS; +typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; +typedef struct NATTraversalInfo_struct NATTraversalInfo; +typedef struct ResourceRecord_struct ResourceRecord; + +// Structure to abstract away the differences between TCP/SSL sockets, and one for UDP sockets +// The actual definition of these structures appear in the appropriate platform support code +typedef struct TCPSocket_struct TCPSocket; +typedef struct UDPSocket_struct UDPSocket; // *************************************************************************** #if 0 +#pragma mark - #pragma mark - DNS Message structures #endif @@ -1312,28 +491,189 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string #define mDNS_numUpdates numAuthorities typedef packedstruct - { - mDNSOpaque16 id; - mDNSOpaque16 flags; - mDNSu16 numQuestions; - mDNSu16 numAnswers; - mDNSu16 numAuthorities; - mDNSu16 numAdditionals; - } DNSMessageHeader; +{ + 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 +#ifndef AbsoluteMaxDNSMessageData #define AbsoluteMaxDNSMessageData 8940 +#endif #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; +{ + DNSMessageHeader h; // Note: Size 12 bytes + mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 +} DNSMessage; + +typedef struct tcpInfo_t +{ + mDNS *m; + TCPSocket *sock; + DNSMessage request; + int requestLen; + DNSQuestion *question; // For queries + AuthRecord *rr; // For record updates + mDNSAddr Addr; + mDNSIPPort Port; + mDNSIPPort SrcPort; + DNSMessage *reply; + mDNSu16 replylen; + unsigned long nread; + int numReplies; +} tcpInfo_t; // *************************************************************************** #if 0 +#pragma mark - +#pragma mark - Other Packet Format Structures +#endif + +typedef packedstruct +{ + mDNSEthAddr dst; + mDNSEthAddr src; + mDNSOpaque16 ethertype; +} EthernetHeader; // 14 bytes + +typedef packedstruct +{ + mDNSOpaque16 hrd; + mDNSOpaque16 pro; + mDNSu8 hln; + mDNSu8 pln; + mDNSOpaque16 op; + mDNSEthAddr sha; + mDNSv4Addr spa; + mDNSEthAddr tha; + mDNSv4Addr tpa; +} ARP_EthIP; // 28 bytes + +typedef packedstruct +{ + mDNSu8 vlen; + mDNSu8 tos; + mDNSu16 totlen; + mDNSOpaque16 id; + mDNSOpaque16 flagsfrags; + mDNSu8 ttl; + mDNSu8 protocol; // Payload type: 0x06 = TCP, 0x11 = UDP + mDNSu16 checksum; + mDNSv4Addr src; + mDNSv4Addr dst; +} IPv4Header; // 20 bytes + +typedef packedstruct +{ + mDNSu32 vcf; // Version, Traffic Class, Flow Label + mDNSu16 len; // Payload Length + mDNSu8 pro; // Type of next header: 0x06 = TCP, 0x11 = UDP, 0x3A = ICMPv6 + mDNSu8 ttl; // Hop Limit + mDNSv6Addr src; + mDNSv6Addr dst; +} IPv6Header; // 40 bytes + +typedef packedstruct +{ + mDNSv6Addr src; + mDNSv6Addr dst; + mDNSOpaque32 len; + mDNSOpaque32 pro; +} IPv6PseudoHeader; // 40 bytes + +typedef union +{ + mDNSu8 bytes[20]; + ARP_EthIP arp; + IPv4Header v4; + IPv6Header v6; +} NetworkLayerPacket; + +typedef packedstruct +{ + mDNSIPPort src; + mDNSIPPort dst; + mDNSu32 seq; + mDNSu32 ack; + mDNSu8 offset; + mDNSu8 flags; + mDNSu16 window; + mDNSu16 checksum; + mDNSu16 urgent; +} TCPHeader; // 20 bytes; IP protocol type 0x06 + +typedef struct +{ + mDNSInterfaceID IntfId; + mDNSu32 seq; + mDNSu32 ack; + mDNSu16 window; +} mDNSTCPInfo; + +typedef packedstruct +{ + mDNSIPPort src; + mDNSIPPort dst; + mDNSu16 len; // Length including UDP header (i.e. minimum value is 8 bytes) + mDNSu16 checksum; +} UDPHeader; // 8 bytes; IP protocol type 0x11 + +typedef packedstruct +{ + mDNSu8 type; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement + mDNSu8 code; + mDNSu16 checksum; + mDNSu32 flags_res; // R/S/O flags and reserved bits + mDNSv6Addr target; + // Typically 8 bytes of options are also present +} IPv6NDP; // 24 bytes or more; IP protocol type 0x3A + +typedef struct +{ + mDNSAddr ipaddr; + char ethaddr[18]; +} IPAddressMACMapping; + +#define NDP_Sol 0x87 +#define NDP_Adv 0x88 + +#define NDP_Router 0x80 +#define NDP_Solicited 0x40 +#define NDP_Override 0x20 + +#define NDP_SrcLL 1 +#define NDP_TgtLL 2 + +typedef union +{ + mDNSu8 bytes[20]; + TCPHeader tcp; + UDPHeader udp; + IPv6NDP ndp; +} TransportLayerPacket; + +typedef packedstruct +{ + mDNSOpaque64 InitiatorCookie; + mDNSOpaque64 ResponderCookie; + mDNSu8 NextPayload; + mDNSu8 Version; + mDNSu8 ExchangeType; + mDNSu8 Flags; + mDNSOpaque32 MessageID; + mDNSu32 Length; +} IKEHeader; // 28 bytes + +// *************************************************************************** +#if 0 +#pragma mark - #pragma mark - Resource Record structures #endif @@ -1387,75 +727,288 @@ typedef packedstruct // 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 6 (value 0x40) is set for answer records; clear for authority/additional records // Bit 5 (value 0x20) is set for records received with the kDNSClass_UniqueRRSet enum - { - kDNSRecordTypeUnregistered = 0x00, // Not currently in any list - kDNSRecordTypeDeregistering = 0x01, // Shared record about to announce its departure and leave the list +{ + 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 + 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 + 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), + kDNSRecordTypeVerified = 0x10, // Unique means mDNS should check that name is unique (and then send immediate responses) + kDNSRecordTypeKnownUnique = 0x20, // Known Unique means mDNS can assume name is unique without checking + // For Dynamic Update records, Known Unique means the record must already exist on the server. + kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveSharedMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared), + kDNSRecordTypeActiveUniqueMask = (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveMask = (kDNSRecordTypeActiveSharedMask | kDNSRecordTypeActiveUniqueMask), - kDNSRecordTypePacketAdd = 0x80, // Received in the Additional Section of a DNS Response - kDNSRecordTypePacketAddUnique = 0x90, // Received in the Additional Section of a DNS Response with kDNSClass_UniqueRRSet set - kDNSRecordTypePacketAuth = 0xA0, // Received in the Authorities Section of a DNS Response - kDNSRecordTypePacketAuthUnique = 0xB0, // Received in the Authorities Section of a DNS Response with kDNSClass_UniqueRRSet set - kDNSRecordTypePacketAns = 0xC0, // Received in the Answer Section of a DNS Response - kDNSRecordTypePacketAnsUnique = 0xD0, // Received in the Answer Section of a DNS Response with kDNSClass_UniqueRRSet set + 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 - }; + kDNSRecordTypePacketNegative = 0xF0, // Pseudo-RR generated to cache non-existence results like NXDomain + + kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique, kDNSRecordTypePacketNegative +}; typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; typedef packedstruct { mDNSu16 preference; domainname exchange; } rdataMX; +typedef packedstruct { domainname mbox; domainname txt; } rdataRP; +typedef packedstruct { mDNSu16 preference; domainname map822; domainname mapx400; } rdataPX; + +typedef packedstruct +{ + domainname mname; + domainname rname; + mDNSs32 serial; // Modular counter; increases when zone changes + mDNSu32 refresh; // Time in seconds that a slave waits after successful replication of the database before it attempts replication again + mDNSu32 retry; // Time in seconds that a slave waits after an unsuccessful replication attempt before it attempts replication again + mDNSu32 expire; // Time in seconds that a slave holds on to old data while replication attempts remain unsuccessful + mDNSu32 min; // Nominally the minimum record TTL for this zone, in seconds; also used for negative caching. +} rdataSOA; + +// http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml +// Algorithm used for RRSIG, DS and DNS KEY +#define CRYPTO_RSA_SHA1 0x05 +#define CRYPTO_DSA_NSEC3_SHA1 0x06 +#define CRYPTO_RSA_NSEC3_SHA1 0x07 +#define CRYPTO_RSA_SHA256 0x08 +#define CRYPTO_RSA_SHA512 0x0A + +#define CRYPTO_ALG_MAX 0x0B + +// alg - same as in RRSIG, DNS KEY or DS. +// RFC 4034 defines SHA1 +// RFC 4509 defines SHA256 +// Note: NSEC3 also uses 1 for SHA1 and hence we will reuse for now till a new +// value is assigned. +// +#define SHA1_DIGEST_TYPE 1 +#define SHA256_DIGEST_TYPE 2 +#define DIGEST_TYPE_MAX 3 + +// We need support for base64 and base32 encoding for displaying KEY, NSEC3 +// To make this platform agnostic, we define two types which the platform +// needs to support +#define ENC_BASE32 1 +#define ENC_BASE64 2 +#define ENC_ALG_MAX 3 + +#define DS_FIXED_SIZE 4 +typedef packedstruct +{ + mDNSu16 keyTag; + mDNSu8 alg; + mDNSu8 digestType; + mDNSu8 *digest; +} rdataDS; + +typedef struct TrustAnchor +{ + struct TrustAnchor *next; + int digestLen; + mDNSu32 validFrom; + mDNSu32 validUntil; + domainname zone; + rdataDS rds; +} TrustAnchor; + +//size of rdataRRSIG excluding signerName and signature (which are variable fields) +#define RRSIG_FIXED_SIZE 18 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; +{ + mDNSu16 typeCovered; + mDNSu8 alg; + mDNSu8 labels; + mDNSu32 origTTL; + mDNSu32 sigExpireTime; + mDNSu32 sigInceptTime; + mDNSu16 keyTag; + mDNSu8 *signerName; + // mDNSu8 *signature +} rdataRRSig; + +// RFC 4034: For DNS Key RR +// flags - the valid value for DNSSEC is 256 (Zone signing key - ZSK) and 257 (Secure Entry Point) which also +// includes the ZSK bit +// +#define DNSKEY_ZONE_SIGN_KEY 0x100 +#define DNSKEY_SECURE_ENTRY_POINT 0x101 + +// proto - the only valid value for protocol is 3 (See RFC 4034) +#define DNSKEY_VALID_PROTO_VALUE 0x003 + +// alg - The only mandatory algorithm that we support is RSA/SHA-1 +// DNSSEC_RSA_SHA1_ALG +#define DNSKEY_FIXED_SIZE 4 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 +{ + mDNSu16 flags; + mDNSu8 proto; + mDNSu8 alg; + mDNSu8 *data; +} rdataDNSKey; + +#define NSEC3_FIXED_SIZE 5 +#define NSEC3_FLAGS_OPTOUT 1 +#define NSEC3_MAX_ITERATIONS 2500 typedef packedstruct - { - mDNSu16 opt; - mDNSu16 optlen; - union { LLQOptData llq; mDNSu32 lease; } OptData; - } rdataOpt; +{ + mDNSu8 alg; + mDNSu8 flags; + mDNSu16 iterations; + mDNSu8 saltLength; + mDNSu8 *salt; + // hashLength, nxt, bitmap +} rdataNSEC3; + +// In the multicast usage of NSEC3, we know the actual size of RData +// 4 bytes : HashAlg, Flags,Iterations +// 5 bytes : Salt Length 1 byte, Salt 4 bytes +// 21 bytes : HashLength 1 byte, Hash 20 bytes +// 34 bytes : Window number, Bitmap length, Type bit map to include the first 256 types +#define MCAST_NSEC3_RDLENGTH (4 + 5 + 21 + 34) +#define SHA1_HASH_LENGTH 20 + +// Base32 encoding takes 5 bytes of the input and encodes as 8 bytes of output. +// For example, SHA-1 hash of 20 bytes will be encoded as 20/5 * 8 = 32 base32 +// bytes. For a max domain name size of 255 bytes of base32 encoding : (255/8)*5 +// is the max hash length possible. +#define NSEC3_MAX_HASH_LEN 155 +// In NSEC3, the names are hashed and stored in the first label and hence cannot exceed label +// size. +#define NSEC3_MAX_B32_LEN MAX_DOMAIN_LABEL + +// We define it here instead of dnssec.h so that these values can be used +// in files without bringing in all of dnssec.h unnecessarily. +typedef enum +{ + DNSSEC_Secure = 1, // Securely validated and has a chain up to the trust anchor + DNSSEC_Insecure, // Cannot build a chain up to the trust anchor + DNSSEC_Indeterminate, // Not used currently + DNSSEC_Bogus, // failed to validate signatures + DNSSEC_NoResponse // No DNSSEC records to start with +} DNSSECStatus; + +#define DNSSECRecordType(rrtype) (((rrtype) == kDNSType_RRSIG) || ((rrtype) == kDNSType_NSEC) || ((rrtype) == kDNSType_DNSKEY) || ((rrtype) == kDNSType_DS) || \ + ((rrtype) == kDNSType_NSEC3)) -// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record +typedef enum +{ + platform_OSX = 1, // OSX Platform + platform_iOS, // iOS Platform + platform_Atv, // Atv Platform + platform_NonApple // Non-Apple (Windows, POSIX) Platform +} Platform_t; + +// EDNS Option Code registrations are recorded in the "DNS EDNS0 Options" section of +// <http://www.iana.org/assignments/dns-parameters> + +#define kDNSOpt_LLQ 1 +#define kDNSOpt_Lease 2 +#define kDNSOpt_NSID 3 +#define kDNSOpt_Owner 4 +#define kDNSOpt_Trace 65001 // 65001-65534 Reserved for Local/Experimental Use + +typedef struct +{ + mDNSu16 vers; + mDNSu16 llqOp; + mDNSu16 err; // Or UDP reply port, in setup request + // Note: In the in-memory form, there's typically a two-byte space here, so that the following 64-bit id is word-aligned + mDNSOpaque64 id; + mDNSu32 llqlease; +} LLQOptData; + +typedef struct +{ + mDNSu8 vers; // Version number of this Owner OPT record + mDNSs8 seq; // Sleep/wake epoch + mDNSEthAddr HMAC; // Host's primary identifier (e.g. MAC of on-board Ethernet) + mDNSEthAddr IMAC; // Interface's MAC address (if different to primary MAC) + mDNSOpaque48 password; // Optional password +} OwnerOptData; + +typedef struct +{ + mDNSu8 platf; // Running platform (see enum Platform_t) + mDNSu32 mDNSv; // mDNSResponder Version (DNS_SD_H defined in dns_sd.h) +} TracerOptData; + +// Note: rdataOPT format may be repeated an arbitrary number of times in a single resource record +typedef packedstruct +{ + mDNSu16 opt; + mDNSu16 optlen; + union { LLQOptData llq; mDNSu32 updatelease; OwnerOptData owner; TracerOptData tracer; } u; +} rdataOPT; + +// Space needed to put OPT records into a packet: +// Header 11 bytes (name 1, type 2, class 2, TTL 4, length 2) +// LLQ rdata 18 bytes (opt 2, len 2, vers 2, op 2, err 2, id 8, lease 4) +// Lease rdata 8 bytes (opt 2, len 2, lease 4) +// Owner rdata 12-24 bytes (opt 2, len 2, owner 8-20) +// Trace rdata 9 bytes (opt 2, len 2, platf 1, mDNSv 4) + + +#define DNSOpt_Header_Space 11 +#define DNSOpt_LLQData_Space (4 + 2 + 2 + 2 + 8 + 4) +#define DNSOpt_LeaseData_Space (4 + 4) +#define DNSOpt_OwnerData_ID_Space (4 + 2 + 6) +#define DNSOpt_OwnerData_ID_Wake_Space (4 + 2 + 6 + 6) +#define DNSOpt_OwnerData_ID_Wake_PW4_Space (4 + 2 + 6 + 6 + 4) +#define DNSOpt_OwnerData_ID_Wake_PW6_Space (4 + 2 + 6 + 6 + 6) +#define DNSOpt_TraceData_Space (4 + 1 + 4) + +#define ValidOwnerLength(X) ( (X) == DNSOpt_OwnerData_ID_Space - 4 || \ + (X) == DNSOpt_OwnerData_ID_Wake_Space - 4 || \ + (X) == DNSOpt_OwnerData_ID_Wake_PW4_Space - 4 || \ + (X) == DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 ) + +#define DNSOpt_Owner_Space(A,B) (mDNSSameEthAddress((A),(B)) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space) + +#define DNSOpt_Data_Space(O) ( \ + (O)->opt == kDNSOpt_LLQ ? DNSOpt_LLQData_Space : \ + (O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space : \ + (O)->opt == kDNSOpt_Trace ? DNSOpt_TraceData_Space : \ + (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000) + +// NSEC record is defined in RFC 4034. +// 16 bit RRTYPE space is split into 256 windows and each window has 256 bits (32 bytes). +// If we create a structure for NSEC, it's size would be: +// +// 256 bytes domainname 'nextname' +// + 256 * 34 = 8704 bytes of bitmap data +// = 8960 bytes total +// +// This would be a waste, as types about 256 are not very common. But it would be odd, if we receive +// a type above 256 (.US zone had TYPE65534 when this code was written) and not able to handle it. +// Hence, we handle any size by not fixing a strucure in place. The following is just a placeholder +// and never used anywhere. +// +#define NSEC_MCAST_WINDOW_SIZE 32 +typedef struct +{ + domainname *next; //placeholders are uncommented because C89 in Windows requires that a struct has at least a member. + char bitmap[32]; +} rdataNSEC; + +// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes) // MaximumRDSize is 8K the absolute maximum we support (at least for now) #define StandardAuthRDSize 264 +#ifndef MaximumRDSize #define MaximumRDSize 8192 +#endif // InlineCacheRDSize is 68 // Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object @@ -1471,787 +1024,1541 @@ typedef packedstruct // 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 - +// The RDataBody union defines the common rdata types that fit into our 264-byte limit +typedef union +{ + mDNSu8 data[StandardAuthRDSize]; + mDNSv4Addr ipv4; // For 'A' record + domainname name; // For PTR, NS, CNAME, DNAME + UTF8str255 txt; + rdataMX mx; + mDNSv6Addr ipv6; // For 'AAAA' record + rdataSRV srv; + rdataOPT opt[2]; // For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together +} RDataBody; + +// The RDataBody2 union is the same as above, except it includes fields for the larger types like soa, rp, px typedef union - { - mDNSu8 data[StandardAuthRDSize]; - mDNSv4Addr ipv4; // For 'A' record - 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; +{ + mDNSu8 data[StandardAuthRDSize]; + mDNSv4Addr ipv4; // For 'A' record + domainname name; // For PTR, NS, CNAME, DNAME + rdataSOA soa; // This is large; not included in the normal RDataBody definition + UTF8str255 txt; + rdataMX mx; + rdataRP rp; // This is large; not included in the normal RDataBody definition + rdataPX px; // This is large; not included in the normal RDataBody definition + mDNSv6Addr ipv6; // For 'AAAA' record + rdataSRV srv; + rdataOPT opt[2]; // For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together + rdataDS ds; + rdataDNSKey key; + rdataRRSig rrsig; +} RDataBody2; typedef struct - { - mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody)) - RDataBody u; - } RData; +{ + mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody)) + mDNSu16 padding; // So that RDataBody is aligned on 32-bit boundary + RDataBody u; +} RData; + +// sizeofRDataHeader should be 4 bytes #define sizeofRDataHeader (sizeof(RData) - sizeof(RDataBody)) -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; +// RData_small is a smaller version of the RData object, used for inline data storage embedded in a CacheRecord_struct +typedef struct +{ + mDNSu16 MaxRDLength; // Storage allocated for data (may be greater than InlineCacheRDSize if additional storage follows this object) + mDNSu16 padding; // So that data is aligned on 32-bit boundary + mDNSu8 data[InlineCacheRDSize]; +} RData_small; -// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() -typedef void mDNSRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result); +// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() +typedef void mDNSRecordCallback (mDNS *const m, AuthRecord *const rr, mStatus result); // Note: // Restrictions: An mDNSRecordUpdateCallback may not make any mDNS API calls. // The intent of this callback is to allow the client to free memory, if necessary. // The internal data structures of the mDNS code may not be in a state where mDNS API calls may be made safely. -typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData); +typedef void mDNSRecordUpdateCallback (mDNS *const m, AuthRecord *const rr, RData *OldRData, mDNSu16 OldRDLen); + +// *************************************************************************** +#if 0 +#pragma mark - +#pragma mark - NAT Traversal structures and constants +#endif + +#define NATMAP_MAX_RETRY_INTERVAL ((mDNSPlatformOneSecond * 60) * 15) // Max retry interval is 15 minutes +#define NATMAP_MIN_RETRY_INTERVAL (mDNSPlatformOneSecond * 2) // Min retry interval is 2 seconds +#define NATMAP_INIT_RETRY (mDNSPlatformOneSecond / 4) // start at 250ms w/ exponential decay +#define NATMAP_DEFAULT_LEASE (60 * 60 * 2) // 2 hour lease life in seconds +#define NATMAP_VERS 0 -typedef 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 +{ + NATOp_AddrRequest = 0, + NATOp_MapUDP = 1, + NATOp_MapTCP = 2, + + NATOp_AddrResponse = 0x80 | 0, + NATOp_MapUDPResponse = 0x80 | 1, + NATOp_MapTCPResponse = 0x80 | 2, +} NATOp_t; + +enum +{ + NATErr_None = 0, + NATErr_Vers = 1, + NATErr_Refused = 2, + NATErr_NetFail = 3, + NATErr_Res = 4, + NATErr_Opcode = 5 +}; + +typedef mDNSu16 NATErr_t; + +typedef packedstruct +{ + mDNSu8 vers; + mDNSu8 opcode; +} NATAddrRequest; + +typedef packedstruct +{ + mDNSu8 vers; + mDNSu8 opcode; + mDNSu16 err; + mDNSu32 upseconds; // Time since last NAT engine reboot, in seconds + mDNSv4Addr ExtAddr; +} NATAddrReply; + +typedef packedstruct +{ + mDNSu8 vers; + mDNSu8 opcode; + mDNSOpaque16 unused; + mDNSIPPort intport; + mDNSIPPort extport; + mDNSu32 NATReq_lease; +} NATPortMapRequest; + +typedef packedstruct +{ + mDNSu8 vers; + mDNSu8 opcode; + mDNSu16 err; + mDNSu32 upseconds; // Time since last NAT engine reboot, in seconds + mDNSIPPort intport; + mDNSIPPort extport; + mDNSu32 NATRep_lease; +} NATPortMapReply; + +// PCP Support for IPv4 mappings + +#define PCP_VERS 0x02 +#define PCP_WAITSECS_AFTER_EPOCH_INVALID 5 + +typedef enum +{ + PCPOp_Announce = 0, + PCPOp_Map = 1 +} PCPOp_t; + +typedef enum +{ + PCPProto_All = 0, + PCPProto_TCP = 6, + PCPProto_UDP = 17 +} PCPProto_t; + +typedef enum +{ + PCPResult_Success = 0, + PCPResult_UnsuppVersion = 1, + PCPResult_NotAuthorized = 2, + PCPResult_MalformedReq = 3, + PCPResult_UnsuppOpcode = 4, + PCPResult_UnsuppOption = 5, + PCPResult_MalformedOption = 6, + PCPResult_NetworkFailure = 7, + PCPResult_NoResources = 8, + PCPResult_UnsuppProtocol = 9, + PCPResult_UserExQuota = 10, + PCPResult_CantProvideExt = 11, + PCPResult_AddrMismatch = 12, + PCPResult_ExcesRemotePeer = 13 +} PCPResult_t; + +typedef packedstruct +{ + mDNSu8 version; + mDNSu8 opCode; + mDNSOpaque16 reserved; + mDNSu32 lifetime; + mDNSv6Addr clientAddr; + mDNSu32 nonce[3]; + mDNSu8 protocol; + mDNSu8 reservedMapOp[3]; + mDNSIPPort intPort; + mDNSIPPort extPort; + mDNSv6Addr extAddress; +} PCPMapRequest; + +typedef packedstruct +{ + mDNSu8 version; + mDNSu8 opCode; + mDNSu8 reserved; + mDNSu8 result; + mDNSu32 lifetime; + mDNSu32 epoch; + mDNSu32 clientAddrParts[3]; + mDNSu32 nonce[3]; + mDNSu8 protocol; + mDNSu8 reservedMapOp[3]; + mDNSIPPort intPort; + mDNSIPPort extPort; + mDNSv6Addr extAddress; +} PCPMapReply; + +// LNT Support + +typedef enum +{ + LNTDiscoveryOp = 1, + LNTExternalAddrOp = 2, + LNTPortMapOp = 3, + LNTPortMapDeleteOp = 4 +} LNTOp_t; + +#define LNT_MAXBUFSIZE 8192 +typedef struct tcpLNTInfo_struct tcpLNTInfo; +struct tcpLNTInfo_struct +{ + tcpLNTInfo *next; + mDNS *m; + NATTraversalInfo *parentNATInfo; // pointer back to the parent NATTraversalInfo + TCPSocket *sock; + LNTOp_t op; // operation performed using this connection + mDNSAddr Address; // router address + mDNSIPPort Port; // router port + mDNSu8 *Request; // xml request to router + int requestLen; + mDNSu8 *Reply; // xml reply from router + int replyLen; + unsigned long nread; // number of bytes read so far + int retries; // number of times we've tried to do this port mapping +}; + +typedef void (*NATTraversalClientCallback)(mDNS *m, NATTraversalInfo *n); + +// if m->timenow < ExpiryTime then we have an active mapping, and we'll renew halfway to expiry +// if m->timenow >= ExpiryTime then our mapping has expired, and we're trying to create one + +typedef enum +{ + NATTProtocolNone = 0, + NATTProtocolNATPMP = 1, + NATTProtocolUPNPIGD = 2, + NATTProtocolPCP = 3, +} NATTProtocol; + +struct NATTraversalInfo_struct +{ + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + NATTraversalInfo *next; + + mDNSs32 ExpiryTime; // Time this mapping expires, or zero if no mapping + mDNSs32 retryInterval; // Current interval, between last packet we sent and the next one + mDNSs32 retryPortMap; // If Protocol is nonzero, time to send our next mapping packet + mStatus NewResult; // New error code; will be copied to Result just prior to invoking callback + NATTProtocol lastSuccessfulProtocol; // To send correct deletion request & update non-PCP external address operations + mDNSBool sentNATPMP; // Whether we just sent a NAT-PMP packet, so we won't send another if + // we receive another NAT-PMP "Unsupported Version" packet + +#ifdef _LEGACY_NAT_TRAVERSAL_ + tcpLNTInfo tcpInfo; // Legacy NAT traversal (UPnP) TCP connection +#endif + + // Result fields: When the callback is invoked these fields contain the answers the client is looking for + // When the callback is invoked ExternalPort is *usually* set to be the same the same as RequestedPort, except: + // (a) When we're behind a NAT gateway with port mapping disabled, ExternalPort is reported as zero to + // indicate that we don't currently have a working mapping (but RequestedPort retains the external port + // we'd like to get, the next time we meet an accomodating NAT gateway willing to give us one). + // (b) When we have a routable non-RFC1918 address, we don't *need* a port mapping, so ExternalPort + // is reported as the same as our InternalPort, since that is effectively our externally-visible port too. + // Again, RequestedPort retains the external port we'd like to get the next time we find ourself behind a NAT gateway. + // To improve stability of port mappings, RequestedPort is updated any time we get a successful + // mapping response from the PCP, NAT-PMP or UPnP gateway. For example, if we ask for port 80, and + // get assigned port 81, then thereafter we'll contine asking for port 81. + mDNSInterfaceID InterfaceID; + mDNSv4Addr ExternalAddress; // Initially set to onesIPv4Addr, until first callback + mDNSv4Addr NewAddress; // May be updated with actual value assigned by gateway + mDNSIPPort ExternalPort; + mDNSu32 Lifetime; + mStatus Result; + + // Client API fields: The client must set up these fields *before* making any NAT traversal API calls + mDNSu8 Protocol; // NATOp_MapUDP or NATOp_MapTCP, or zero if just requesting the external IP address + mDNSIPPort IntPort; // Client's internal port number (doesn't change) + mDNSIPPort RequestedPort; // Requested external port; may be updated with actual value assigned by gateway + mDNSu32 NATLease; // Requested lifetime in seconds (doesn't change) + NATTraversalClientCallback clientCallback; + void *clientContext; +}; + +// *************************************************************************** +#if 0 +#pragma mark - +#pragma mark - DNSServer & McastResolver structures and constants +#endif + +enum +{ + DNSServer_Untested = 0, + DNSServer_Passed = 1, + DNSServer_Failed = 2, + DNSServer_Disabled = 3 +}; + +enum +{ + DNSServer_FlagDelete = 1, + DNSServer_FlagNew = 2 +}; + +enum +{ + McastResolver_FlagDelete = 1, + McastResolver_FlagNew = 2 +}; + +typedef struct McastResolver +{ + struct McastResolver *next; + mDNSInterfaceID interface; + mDNSu32 flags; // Set when we're planning to delete this from the list + domainname domain; + mDNSu32 timeout; // timeout value for questions +} McastResolver; + +// scoped values for DNSServer matching +enum +{ + kScopeNone = 0, // DNS server used by unscoped questions + kScopeInterfaceID = 1, // Scoped DNS server used only by scoped questions + kScopeServiceID = 2 // Service specific DNS server used only by questions + // have a matching serviceID +}; + +// Note: DNSSECAware is set if we are able to get a valid response to +// a DNSSEC question. In some cases it is possible that the proxy +// strips the EDNS0 option and we just get a plain response with no +// signatures. But we still mark DNSSECAware in that case. As DNSSECAware +// is only used to determine whether DNSSEC_VALIDATION_SECURE_OPTIONAL +// should be turned off or not, it is sufficient that we are getting +// responses back. +typedef struct DNSServer +{ + struct DNSServer *next; + mDNSInterfaceID interface; // DNS requests should be sent on this interface + mDNSs32 serviceID; + mDNSAddr addr; + mDNSIPPort port; + mDNSOpaque16 testid; + mDNSu32 flags; // Set when we're planning to delete this from the list + mDNSu32 teststate; // Have we sent bug-detection query to this server? + mDNSs32 lasttest; // Time we sent last bug-detection query to this server + domainname domain; // name->server matching for "split dns" + mDNSs32 penaltyTime; // amount of time this server is penalized + mDNSu32 scoped; // See the scoped enum above + mDNSu32 timeout; // timeout value for questions + mDNSBool cellIntf; // Resolver from Cellular Interface ? + mDNSu16 resGroupID; // ID of the resolver group that contains this DNSServer + mDNSBool req_A; // If set, send v4 query (DNSConfig allows A queries) + mDNSBool req_AAAA; // If set, send v6 query (DNSConfig allows AAAA queries) + mDNSBool req_DO; // If set, okay to send DNSSEC queries (EDNS DO bit is supported) + mDNSBool retransDO; // Total Retransmissions for queries sent with DO option + mDNSBool DNSSECAware; // set if we are able to receive a response to a request + // sent with DO option. +} DNSServer; + 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 +{ + mDNSu8 *AnonData; + int AnonDataLen; + mDNSu32 salt; + ResourceRecord *nsec3RR; + mDNSInterfaceID SendNow; // The interface ID that this record should be sent on +} AnonymousInfo; + +struct ResourceRecord_struct +{ + mDNSu8 RecordType; // See enum above + mDNSu16 rrtype; + mDNSu16 rrclass; + mDNSu32 rroriginalttl; // In seconds + mDNSu16 rdlength; // Size of the raw rdata, in bytes, in the on-the-wire format + // (In-memory storage may be larger, for structures containing 'holes', like SOA) + mDNSu16 rdestimate; // Upper bound on on-the-wire size of rdata after name compression + mDNSu32 namehash; // Name-based (i.e. case-insensitive) hash of name + mDNSu32 rdatahash; // For rdata containing domain name (e.g. PTR, SRV, CNAME etc.), case-insensitive name hash + // else, for all other rdata, 32-bit hash of the raw rdata + // Note: This requirement is important. Various routines like AddAdditionalsToResponseList(), + // ReconfirmAntecedents(), etc., use rdatahash as a pre-flight check to see + // whether it's worth doing a full SameDomainName() call. If the rdatahash + // is not a correct case-insensitive name hash, they'll get false negatives. + + // Grouping pointers together at the end of the structure improves the memory layout efficiency + mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface + // For records received off the wire, InterfaceID is *always* set to the receiving interface + // For our authoritative records, InterfaceID is usually zero, except for those few records + // that are interface-specific (e.g. address records, especially linklocal addresses) + const domainname *name; + RData *rdata; // Pointer to storage for this rdata + DNSServer *rDNSServer; // Unicast DNS server authoritative for this entry;null for multicast + AnonymousInfo *AnonInfo; // Anonymous Information +}; + + +// Unless otherwise noted, states may apply to either independent record registrations or service registrations +typedef enum +{ + regState_Zero = 0, + regState_Pending = 1, // update sent, reply not received + regState_Registered = 2, // update sent, reply received + regState_DeregPending = 3, // dereg sent, reply not received + regState_Unregistered = 4, // not in any list + regState_Refresh = 5, // outstanding refresh (or target change) message + regState_NATMap = 6, // establishing NAT port mapping + regState_UpdatePending = 7, // update in flight as result of mDNS_Update call + regState_NoTarget = 8, // SRV Record registration pending registration of hostname + regState_NATError = 9 // unable to complete NAT traversal +} regState_t; - // 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; +enum +{ + Target_Manual = 0, + Target_AutoHost = 1, + Target_AutoHostAndNATMAP = 2 +}; + +typedef enum +{ + mergeState_Zero = 0, + mergeState_DontMerge = 1 // Set on fatal error conditions to disable merging +} mergeState_t; + +#define AUTH_GROUP_NAME_SIZE 128 +struct AuthGroup_struct // Header object for a list of AuthRecords with the same name +{ + AuthGroup *next; // Next AuthGroup object in this hash table bucket + mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name + AuthRecord *members; // List of CacheRecords with this same name + AuthRecord **rrauth_tail; // Tail end of that list + domainname *name; // Common name for all AuthRecords in this list + AuthRecord *NewLocalOnlyRecords; + mDNSu8 namestorage[AUTH_GROUP_NAME_SIZE]; +}; + +#ifndef AUTH_HASH_SLOTS +#define AUTH_HASH_SLOTS 499 +#endif +#define FORALL_AUTHRECORDS(SLOT,AG,AR) \ + for ((SLOT) = 0; (SLOT) < AUTH_HASH_SLOTS; (SLOT)++) \ + for ((AG)=m->rrauth.rrauth_hash[(SLOT)]; (AG); (AG)=(AG)->next) \ + for ((AR) = (AG)->members; (AR); (AR)=(AR)->next) + +typedef union AuthEntity_union AuthEntity; +union AuthEntity_union { AuthEntity *next; AuthGroup ag; }; +typedef struct { + mDNSu32 rrauth_size; // Total number of available auth entries + mDNSu32 rrauth_totalused; // Number of auth entries currently occupied + mDNSu32 rrauth_report; + mDNSu8 rrauth_lock; // For debugging: Set at times when these lists may not be modified + AuthEntity *rrauth_free; + AuthGroup *rrauth_hash[AUTH_HASH_SLOTS]; +}AuthHash; + +// AuthRecordAny includes mDNSInterface_Any and interface specific auth records. +typedef enum +{ + AuthRecordAny, // registered for *Any, NOT including P2P interfaces + AuthRecordAnyIncludeP2P, // registered for *Any, including P2P interfaces + AuthRecordAnyIncludeAWDL, // registered for *Any, including AWDL interface + AuthRecordAnyIncludeAWDLandP2P, // registered for *Any, including AWDL and P2P interfaces + AuthRecordLocalOnly, + AuthRecordP2P // discovered over D2D/P2P framework +} AuthRecType; + +typedef enum +{ + AuthFlagsWakeOnly = 0x1 // WakeOnly service +} AuthRecordFlags; 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 +{ + // For examples of how to set up this structure for use in mDNS_Register(), + // see mDNS_AdvertiseInterface() or mDNS_RegisterService(). + // Basically, resrec and persistent metadata need to be set up before calling mDNS_Register(). + // mDNS_SetupResourceRecord() is avaliable as a helper routine to set up most fields to sensible default values for you + + AuthRecord *next; // Next in list; first element of structure for efficiency reasons + // Field Group 1: Common ResourceRecord fields + ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit + + // Field Group 2: Persistent metadata for Authoritative Records + AuthRecord *Additional1; // Recommended additional record to include in response (e.g. SRV for PTR record) + AuthRecord *Additional2; // Another additional (e.g. TXT for PTR record) + AuthRecord *DependentOn; // This record depends on another for its uniqueness checking + AuthRecord *RRSet; // This unique record is part of an RRSet + mDNSRecordCallback *RecordCallback; // Callback function to call for state changes, and to free memory asynchronously on deregistration + void *RecordContext; // Context parameter for the callback function + mDNSu8 AutoTarget; // Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name + mDNSu8 AllowRemoteQuery; // Set if we allow hosts not on the local link to query this record + mDNSu8 ForceMCast; // Set by client to advertise solely via multicast, even for apparently unicast names + mDNSu8 AuthFlags; + + OwnerOptData WakeUp; // WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record + mDNSAddr AddressProxy; // For reverse-mapping Sleep Proxy PTR records, address in question + mDNSs32 TimeRcvd; // In platform time units + mDNSs32 TimeExpire; // In platform time units + AuthRecType ARType; // LocalOnly, P2P or Normal ? + mDNSs32 KATimeExpire; // In platform time units: time to send keepalive packet for the proxy record + + // Field Group 3: Transient state for Authoritative Records + mDNSu8 Acknowledged; // Set if we've given the success callback to the client + mDNSu8 ProbeRestartCount; // Number of times we have restarted probing + mDNSu8 ProbeCount; // Number of probes remaining before this record is valid (kDNSRecordTypeUnique) + mDNSu8 AnnounceCount; // Number of announcements remaining (kDNSRecordTypeShared) + mDNSu8 RequireGoodbye; // Set if this RR has been announced on the wire and will require a goodbye packet + mDNSu8 AnsweredLocalQ; // Set if this AuthRecord has been delivered to any local question (LocalOnly or mDNSInterface_Any) + mDNSu8 IncludeInProbe; // Set if this RR is being put into a probe right now + mDNSu8 ImmedUnicast; // Set if we may send our response directly via unicast to the requester + mDNSInterfaceID SendNSECNow; // Set if we need to generate associated NSEC data for this rrname + mDNSInterfaceID ImmedAnswer; // Someone on this interface issued a query we need to answer (all-ones for all interfaces) #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - mDNSs32 ImmedAnswerMarkTime; + 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 - }; + mDNSInterfaceID ImmedAdditional; // Hint that we might want to also send this record, just to be helpful + mDNSInterfaceID SendRNow; // The interface this query is being sent on right now + mDNSv4Addr v4Requester; // Recent v4 query for this record, or all-ones if more than one recent query + mDNSv6Addr v6Requester; // Recent v6 query for this record, or all-ones if more than one recent query + AuthRecord *NextResponse; // Link to the next element in the chain of responses to generate + const mDNSu8 *NR_AnswerTo; // Set if this record was selected by virtue of being a direct answer to a question + AuthRecord *NR_AdditionalTo; // Set if this record was selected by virtue of being additional to another + mDNSs32 ThisAPInterval; // In platform time units: Current interval for announce/probe + mDNSs32 LastAPTime; // In platform time units: Last time we sent announcement/probe + mDNSs32 LastMCTime; // Last time we multicast this record (used to guard against packet-storm attacks) + mDNSInterfaceID LastMCInterface; // Interface this record was multicast on at the time LastMCTime was recorded + RData *NewRData; // Set if we are updating this record with new rdata + mDNSu16 newrdlength; // ... and the length of the new RData + mDNSRecordUpdateCallback *UpdateCallback; + mDNSu32 UpdateCredits; // Token-bucket rate limiting of excessive updates + mDNSs32 NextUpdateCredit; // Time next token is added to bucket + mDNSs32 UpdateBlocked; // Set if update delaying is in effect + + // Field Group 4: Transient uDNS state for Authoritative Records + regState_t state; // Maybe combine this with resrec.RecordType state? Right now it's ambiguous and confusing. + // e.g. rr->resrec.RecordType can be kDNSRecordTypeUnregistered, + // and rr->state can be regState_Unregistered + // What if we find one of those statements is true and the other false? What does that mean? + mDNSBool uselease; // dynamic update contains (should contain) lease option + mDNSs32 expire; // In platform time units: expiration of lease (-1 for static) + mDNSBool Private; // If zone is private, DNS updates may have to be encrypted to prevent eavesdropping + mDNSOpaque16 updateid; // Identifier to match update request and response -- also used when transferring records to Sleep Proxy + mDNSOpaque64 updateIntID; // Interface IDs (one bit per interface index)to which updates have been sent + const domainname *zone; // the zone that is updated + ZoneData *nta; + struct tcpInfo_t *tcp; + NATTraversalInfo NATinfo; + mDNSBool SRVChanged; // temporarily deregistered service because its SRV target or port changed + mergeState_t mState; // Unicast Record Registrations merge state + mDNSu8 refreshCount; // Number of refreshes to the server + mStatus updateError; // Record update resulted in Error ? + + // uDNS_UpdateRecord support fields + // Do we really need all these in *addition* to NewRData and newrdlength above? + void *UpdateContext; // Context parameter for the update callback function + mDNSu16 OrigRDLen; // previously registered, being deleted + mDNSu16 InFlightRDLen; // currently being registered + mDNSu16 QueuedRDLen; // pending operation (re-transmitting if necessary) THEN register the queued update + RData *OrigRData; + RData *InFlightRData; + RData *QueuedRData; + + // Field Group 5: Large data objects go at the end + domainname namestorage; + RData rdatastorage; // Normally the storage is right here, except for oversized records + // rdatastorage MUST be the last thing in the structure -- when using oversized AuthRecords, extra bytes + // are appended after the end of the AuthRecord, logically augmenting the size of the rdatastorage + // DO NOT ADD ANY MORE FIELDS HERE +}; + +// IsLocalDomain alone is not sufficient to determine that a record is mDNS or uDNS. By default domain names within +// the "local" pseudo-TLD (and within the IPv4 and IPv6 link-local reverse mapping domains) are automatically treated +// as mDNS records, but it is also possible to force any record (even those not within one of the inherently local +// domains) to be handled as an mDNS record by setting the ForceMCast flag, or by setting a non-zero InterfaceID. +// For example, the reverse-mapping PTR record created in AdvertiseInterface sets the ForceMCast flag, since it points to +// a dot-local hostname, and therefore it would make no sense to register this record with a wide-area Unicast DNS server. +// The same applies to Sleep Proxy records, which we will answer for when queried via mDNS, but we never want to try +// to register them with a wide-area Unicast DNS server -- and we probably don't have the required credentials anyway. +// Currently we have no concept of a wide-area uDNS record scoped to a particular interface, so if the InterfaceID is +// nonzero we treat this the same as ForceMCast. +// Note: Question_uDNS(Q) is used in *only* one place -- on entry to mDNS_StartQuery_internal, to decide whether to set TargetQID. +// Everywhere else in the code, the determination of whether a question is unicast is made by checking to see if TargetQID is nonzero. +#define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name)) +#define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || (Q)->ProxyQuestion || \ + ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))) + +#define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P) + +#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P || (rr)->ARType == AuthRecordAnyIncludeAWDL || (rr)->ARType == AuthRecordAnyIncludeAWDLandP2P) + +// Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address +// is not available locally for A or AAAA question respectively. Also, if the +// query is disallowed for the "pid" that we are sending on behalf of, suppress it. +#define QuerySuppressed(Q) (((Q)->SuppressUnusable && (Q)->SuppressQuery) || ((Q)->DisallowPID)) + +#define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel) + +// Normally we always lookup the cache and /etc/hosts before sending the query on the wire. For single label +// queries (A and AAAA) that are unqualified (indicated by AppendSearchDomains), we want to append search +// domains before we try them as such +#define ApplySearchDomainsFirst(q) ((q)->AppendSearchDomains && (CountLabels(&((q)->qname))) == 1) // Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field typedef struct ARListElem - { - struct ARListElem *next; - AuthRecord ar; // Note: Must be last 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 ARListElem *next; + AuthRecord ar; // Note: Must be last element of structure, to accomodate oversized AuthRecords +} ARListElem; 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 - }; +{ + CacheRecord *next; // Next in list; first element of structure for efficiency reasons + ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit + + // Transient state for Cache Records + CacheRecord *NextInKAList; // Link to the next element in the chain of known answers to send + mDNSs32 TimeRcvd; // In platform time units + mDNSs32 DelayDelivery; // Set if we want to defer delivery of this answer to local clients + mDNSs32 NextRequiredQuery; // In platform time units + mDNSs32 LastUsed; // In platform time units + DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer. Can never point to a NewQuestion. + mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries + mDNSu8 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer + mDNSu8 CRDNSSECQuestion; // Set to 1 if this was created in response to a DNSSEC question + mDNSOpaque16 responseFlags; // Second 16 bit in the DNS response +#if ENABLE_MULTI_PACKET_QUERY_SNOOPING + mDNSu32 MPUnansweredQ; // Multi-packet query handling: Number of times we've seen a query for this record + mDNSs32 MPLastUnansweredQT; // Multi-packet query handling: Last time we incremented MPUnansweredQ + mDNSu32 MPUnansweredKA; // Multi-packet query handling: Number of times we've seen this record in a KA list + mDNSBool MPExpectingKA; // Multi-packet query handling: Set when we increment MPUnansweredQ; allows one KA +#endif + CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set + CacheRecord *nsec; // NSEC records needed for non-existence proofs + CacheRecord *soa; // SOA record to return for proxy questions + + mDNSAddr sourceAddress; // node from which we received this record + // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit + RData_small smallrdatastorage; // Storage for small records is right here (4 bytes header + 68 bytes data = 72 bytes) +}; + +// Should match the CacheGroup_struct members, except namestorage[]. Only used to calculate +// the size of the namestorage array in CacheGroup_struct so that +// sizeof(CacheGroup) == sizeof(CacheRecord) +struct CacheGroup_base +{ + CacheGroup *next; + mDNSu32 namehash; + CacheRecord *members; + CacheRecord **rrcache_tail; + domainname *name; +}; + +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[sizeof(CacheRecord) - sizeof(struct CacheGroup_base)]; // match sizeof(CacheRecord) +}; // Storage sufficient to hold either a CacheGroup header or a CacheRecord +// -- for best efficiency (to avoid wasted unused storage) they should be the same size typedef union CacheEntity_union CacheEntity; union CacheEntity_union { CacheEntity *next; CacheGroup cg; CacheRecord cr; }; typedef struct - { - CacheRecord r; - mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes - domainname namestorage; // Needs to go *after* the extra rdata bytes - } LargeCacheRecord; - -typedef struct uDNS_HostnameInfo - { - struct uDNS_HostnameInfo *next; +{ + CacheRecord r; + mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes + domainname namestorage; // Needs to go *after* the extra rdata bytes +} LargeCacheRecord; + +typedef struct HostnameInfo +{ + struct HostnameInfo *next; + NATTraversalInfo natinfo; domainname fqdn; - AuthRecord *arv4; // registered IPv4 address record - AuthRecord *arv6; // registered IPv6 address record - mDNSRecordCallback *StatusCallback; // callback to deliver success or error code to client layer - const void *StatusContext; // Client Context - } 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 } ? - }; + AuthRecord arv4; // registered IPv4 address record + AuthRecord arv6; // registered IPv6 address record + mDNSRecordCallback *StatusCallback; // callback to deliver success or error code to client layer + const void *StatusContext; // Client Context +} HostnameInfo; typedef struct ExtraResourceRecord_struct ExtraResourceRecord; struct ExtraResourceRecord_struct - { - ExtraResourceRecord *next; +{ + 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 - }; + AuthRecord r; + // Note: Add any additional fields *before* the AuthRecord in this structure, not at the end. + // In some cases clients can allocate larger chunks of memory and set r->rdata->MaxRDLength to indicate + // that this extra memory is available, which would result in any fields after the AuthRecord getting smashed +}; + +// Note: Within an mDNSServiceCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() +typedef void mDNSServiceCallback (mDNS *const m, ServiceRecordSet *const sr, mStatus result); + +// A ServiceRecordSet has no special meaning to the core code of the Multicast DNS protocol engine; +// it is just a convenience structure to group together the records that make up a standard service +// registration so that they can be allocted and deallocted together as a single memory object. +// It contains its own ServiceCallback+ServiceContext to report aggregate results up to the next layer of software above. +// It also contains: +// * the basic PTR/SRV/TXT triplet used to represent any DNS-SD service +// * the "_services" PTR record for service enumeration +// * the optional list of SubType PTR records +// * the optional list of additional records attached to the service set (e.g. iChat pictures) -// 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 - }; +{ + // These internal state fields are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_RegisterService(); + // all required data is passed as parameters to that function. + mDNSServiceCallback *ServiceCallback; + void *ServiceContext; + mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict + + ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration + mDNSu32 NumSubTypes; + AuthRecord *SubTypes; + const mDNSu8 *AnonData; + mDNSu32 flags; // saved for subsequent calls to mDNS_RegisterService() if records + // need to be re-registered. + AuthRecord RR_ADV; // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local. + AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. + AuthRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target + AuthRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName + // Don't add any fields after AuthRecord RR_TXT. + // This is where the implicit extra space goes if we allocate a ServiceRecordSet containing an oversized RR_TXT record +}; // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Question structures #endif // We record the last eight instances of each duplicate query -// This gives us v4/v6 on each of Ethernet/AirPort and Firewire, and two free slots "for future expansion" +// 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; +{ + 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_InitialRequest = 1, + LLQ_SecondaryRequest = 2, + LLQ_Established = 3, + LLQ_Poll = 4 +} LLQ_State; // 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); +{ + LLQErr_NoError = 0, + LLQErr_ServFull = 1, + LLQErr_Static = 2, + LLQErr_FormErr = 3, + LLQErr_NoSuchLLQ = 4, + LLQErr_BadVers = 5, + LLQErr_UnknownErr = 6 +}; + +enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 }; + +#define HMAC_LEN 64 +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + +#define AutoTunnelUnregistered(X) ( \ + (X)->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered ) + +// Internal data structure to maintain authentication information +typedef struct DomainAuthInfo +{ + struct DomainAuthInfo *next; + mDNSs32 deltime; // If we're planning to delete this DomainAuthInfo, the time we want it deleted + mDNSBool AutoTunnel; // Whether this is AutoTunnel + AuthRecord AutoTunnelHostRecord; // User-visible hostname; used as SRV target for AutoTunnel services + AuthRecord AutoTunnelTarget; // Opaque hostname of tunnel endpoint; used as SRV target for AutoTunnelService record + AuthRecord AutoTunnelDeviceInfo; // Device info of tunnel endpoint + AuthRecord AutoTunnelService; // Service record (possibly NAT-Mapped) of IKE daemon implementing tunnel endpoint + AuthRecord AutoTunnel6Record; // AutoTunnel AAAA Record obtained from awacsd + mDNSBool AutoTunnelServiceStarted; // Whether a service has been registered in this domain + mDNSv6Addr AutoTunnelInnerAddress; + domainname domain; + domainname keyname; + domainname hostname; + mDNSIPPort port; + char b64keydata[32]; + mDNSu8 keydata_ipad[HMAC_LEN]; // padded key for inner hash rounds + mDNSu8 keydata_opad[HMAC_LEN]; // padded key for outer hash rounds +} DomainAuthInfo; + +// Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() +// Note: Any value other than QC_rmv i.e., any non-zero value will result in kDNSServiceFlagsAdd to the application +// layer. These values are used within mDNSResponder and not sent across to the application. QC_addnocache is for +// delivering a response without adding to the cache. QC_forceresponse is superset of QC_addnocache where in +// addition to not entering in the cache, it also forces the negative response through. +typedef enum { QC_rmv = 0, QC_add, QC_addnocache, QC_forceresponse, QC_dnssec , QC_nodnssec, QC_suppressed } QC_result; +typedef void mDNSQuestionCallback (mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); +typedef void AsyncDispatchFunc(mDNS *const m, void *context); +typedef void DNSSECAuthInfoFreeCallback(mDNS *const m, void *context); +extern void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDispatchFunc func); + +#define NextQSendTime(Q) ((Q)->LastQTime + (Q)->ThisQInterval) +#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) +#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - NextQSendTime(Q) >= 0) + +// q->ValidationStatus is either DNSSECValNotRequired or DNSSECValRequired and then moves onto DNSSECValInProgress. +// When Validation is done, we mark all "DNSSECValInProgress" questions "DNSSECValDone". If we are answering +// questions from /etc/hosts, then we go straight to DNSSECValDone from the initial state. +typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress, DNSSECValDone } DNSSECValState; + +// ValidationRequired can be set to the following values: +// +// SECURE validation is set to determine whether something is secure or bogus +// INSECURE validation is set internally by dnssec code to indicate that it is currently proving something +// is insecure +#define DNSSEC_VALIDATION_NONE 0x00 +#define DNSSEC_VALIDATION_SECURE 0x01 +#define DNSSEC_VALIDATION_SECURE_OPTIONAL 0x02 +#define DNSSEC_VALIDATION_INSECURE 0x03 + +// For both ValidationRequired and ValidatingResponse question, we validate DNSSEC responses. +// For ProxyQuestion with DNSSECOK, we just receive the DNSSEC records to pass them along without +// validation and if the CD bit is not set, we also validate. +#define DNSSECQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse || ((q)->ProxyQuestion && (q)->ProxyDNSSECOK)) + +// ValidatingQuestion is used when we need to know whether we are validating the DNSSEC responses for a question +#define ValidatingQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse) + +#define DNSSECOptionalQuestion(q) ((q)->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL) + +// Given the resource record and the question, should we follow the CNAME ? +#define FollowCNAME(q, rr, AddRecord) (AddRecord && (q)->qtype != kDNSType_CNAME && \ + (rr)->RecordType != kDNSRecordTypePacketNegative && \ + (rr)->rrtype == kDNSType_CNAME) + +// RFC 4122 defines it to be 16 bytes +#define UUID_SIZE 16 + 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; - }; +{ + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + DNSQuestion *next; + mDNSu32 qnamehash; + mDNSs32 DelayAnswering; // Set if we want to defer answering this question until the cache settles + mDNSs32 LastQTime; // Last scheduled transmission of this Q on *all* applicable interfaces + mDNSs32 ThisQInterval; // LastQTime + ThisQInterval is the next scheduled transmission of this Q + // ThisQInterval > 0 for an active question; + // ThisQInterval = 0 for a suspended question that's still in the list + // ThisQInterval = -1 for a cancelled question (should not still be in list) + mDNSs32 ExpectUnicastResp; // Set when we send a query with the kDNSQClass_UnicastResponse bit set + mDNSs32 LastAnswerPktNum; // The sequence number of the last response packet containing an answer to this Q + mDNSu32 RecentAnswerPkts; // Number of answers since the last time we sent this query + mDNSu32 CurrentAnswers; // Number of records currently in the cache that answer this question + mDNSu32 BrowseThreshold; // If we have received at least this number of answers, + // set the next question interval to MaxQuestionInterval + mDNSu32 LargeAnswers; // Number of answers with rdata > 1024 bytes + mDNSu32 UniqueAnswers; // Number of answers received with kDNSClass_UniqueRRSet bit set + mDNSInterfaceID FlappingInterface1; // Set when an interface goes away, to flag if remove events are delivered for this Q + mDNSInterfaceID FlappingInterface2; // Set when an interface goes away, to flag if remove events are delivered for this Q + DomainAuthInfo *AuthInfo; // Non-NULL if query is currently being done using Private DNS + DNSQuestion *DuplicateOf; + DNSQuestion *NextInDQList; + AnonymousInfo *AnonInfo; // Anonymous Information + 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 + mDNSBool CachedAnswerNeedsUpdate; // See SendQueries(). Set if we're sending this question + // because a cached answer needs to be refreshed. + mDNSu32 RequestUnicast; // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set + mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces + mDNSu32 CNAMEReferrals; // Count of how many CNAME redirections we've done + mDNSBool SuppressQuery; // This query should be suppressed and not sent on the wire + mDNSu8 LOAddressAnswers; // Number of answers from the local only auth records that are + // answering A, AAAA, CNAME, or PTR (/etc/hosts) + mDNSu8 WakeOnResolveCount; // Number of wakes that should be sent on resolve + mDNSs32 StopTime; // Time this question should be stopped by giving them a negative answer + + // DNSSEC fields + DNSSECValState ValidationState; // Current state of the Validation process + DNSSECStatus ValidationStatus; // Validation status for "ValidationRequired" questions (dnssec) + mDNSu8 ValidatingResponse; // Question trying to validate a response (dnssec) on behalf of + // ValidationRequired question + void *DNSSECAuthInfo; + DNSSECAuthInfoFreeCallback *DAIFreeCallback; + + // Wide Area fields. These are used internally by the uDNS core (Unicast) + UDPSocket *LocalSocket; + + // |-> DNS Configuration related fields used in uDNS (Subset of Wide Area/Unicast fields) + DNSServer *qDNSServer; // Caching server for this query (in the absence of an SRV saying otherwise) + mDNSOpaque64 validDNSServers; // Valid DNSServers for this question + mDNSu16 noServerResponse; // At least one server did not respond. + mDNSu16 triedAllServersOnce; // Tried all DNS servers once + mDNSu8 unansweredQueries; // The number of unanswered queries to this server + + ZoneData *nta; // Used for getting zone data for private or LLQ query + mDNSAddr servAddr; // Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query + mDNSIPPort servPort; + struct tcpInfo_t *tcp; + mDNSIPPort tcpSrcPort; // Local Port TCP packet received on;need this as tcp struct is disposed + // by tcpCallback before calling into mDNSCoreReceive + mDNSu8 NoAnswer; // Set if we want to suppress answers until tunnel setup has completed + mDNSu8 Restart; // This question should be restarted soon + + // LLQ-specific fields. These fields are only meaningful when LongLived flag is set + LLQ_State state; + mDNSu32 ReqLease; // seconds (relative) + mDNSs32 expire; // ticks (absolute) + mDNSs16 ntries; // for UDP: the number of packets sent for this LLQ state + // for TCP: there is some ambiguity in the use of this variable, but in general, it is + // the number of TCP/TLS connection attempts for this LLQ state, or + // the number of packets sent for this TCP/TLS connection + mDNSOpaque64 id; + + // DNS Proxy fields + mDNSOpaque16 responseFlags; // Temporary place holder for the error we get back from the DNS server + // till we populate in the cache + mDNSBool DisallowPID; // Is the query allowed for the "PID" that we are sending on behalf of ? + mDNSs32 ServiceID; // Service identifier to match against the DNS server + + // 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 + mDNSu32 flags; // flags from original DNSService*() API request. + mDNSAddr Target; // Non-zero if you want to direct queries to a specific unicast target address + mDNSIPPort TargetPort; // Must be set if Target is set + mDNSOpaque16 TargetQID; // Must be set if Target is set + domainname qname; + mDNSu16 qtype; + mDNSu16 qclass; + mDNSBool LongLived; // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer. + mDNSBool ExpectUnique; // Set by client if it's expecting unique RR(s) for this question, not shared RRs + mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names + mDNSBool ReturnIntermed; // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results + mDNSBool SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire + mDNSBool DenyOnCellInterface; // Set by client to suppress uDNS queries on cellular interface + mDNSBool DenyOnExpInterface; // Set by client to suppress uDNS queries on expensive interface + mDNSu8 RetryWithSearchDomains; // Retry with search domains if there is no entry in the cache or AuthRecords + mDNSu8 TimeoutQuestion; // Timeout this question if there is no reply in configured time + mDNSu8 WakeOnResolve; // Send wakeup on resolve + mDNSu8 UseBackgroundTrafficClass; // Set by client to use background traffic class for request + mDNSs8 SearchListIndex; // Index into SearchList; Used by the client layer but not touched by core + mDNSs8 AppendSearchDomains; // Search domains can be appended for this query + mDNSs8 AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query + mDNSu8 ValidationRequired; // Requires DNSSEC validation. + mDNSu8 ProxyQuestion; // Proxy Question + mDNSu8 ProxyDNSSECOK; // Proxy Question with EDNS0 DNSSEC OK bit set + mDNSs32 pid; // Process ID of the client that is requesting the question + mDNSu8 uuid[UUID_SIZE]; // Unique ID of the client that is requesting the question (valid only if pid is zero) + domainname *qnameOrig; // Copy of the original question name if it is not fully qualified + mDNSQuestionCallback *QuestionCallback; + void *QuestionContext; +}; typedef struct - { - // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() - // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. - domainname name; - mDNSInterfaceID InterfaceID; // ID of the interface the response was received on - mDNSAddr ip; // Remote (destination) IP address where this service can be accessed - mDNSIPPort port; // Port where this service can be accessed - mDNSu16 TXTlen; - mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) - } ServiceInfo; - -// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +{ + // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() + // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. + domainname name; + mDNSInterfaceID InterfaceID; // ID of the interface the response was received on + mDNSAddr ip; // Remote (destination) IP address where this service can be accessed + mDNSIPPort port; // Port where this service can be accessed + mDNSu16 TXTlen; + mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) +} ServiceInfo; + +// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() typedef struct ServiceInfoQuery_struct ServiceInfoQuery; -typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); +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; - }; +{ + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); + // all required data is passed as parameters to that function. + // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information + // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may + // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. + DNSQuestion qSRV; + DNSQuestion qTXT; + DNSQuestion qAv4; + DNSQuestion qAv6; + mDNSu8 GotSRV; + mDNSu8 GotTXT; + mDNSu8 GotADD; + mDNSu32 Answers; + ServiceInfo *info; + mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; + void *ServiceInfoQueryContext; +}; + +typedef enum { ZoneServiceUpdate, ZoneServiceQuery, ZoneServiceLLQ } ZoneService; + +typedef void ZoneDataCallback (mDNS *const m, mStatus err, const ZoneData *result); + +struct ZoneData_struct +{ + domainname ChildName; // Name for which we're trying to find the responsible server + ZoneService ZoneService; // Which service we're seeking for this zone (update, query, or LLQ) + domainname *CurrentSOA; // Points to somewhere within ChildName + domainname ZoneName; // Discovered result: Left-hand-side of SOA record + mDNSu16 ZoneClass; // Discovered result: DNS Class from SOA record + domainname Host; // Discovered result: Target host from SRV record + mDNSIPPort Port; // Discovered result: Update port, query port, or LLQ port from SRV record + mDNSAddr Addr; // Discovered result: Address of Target host from SRV record + mDNSBool ZonePrivate; // Discovered result: Does zone require encrypted queries? + ZoneDataCallback *ZoneDataCallback; // Caller-specified function to be called upon completion + void *ZoneDataContext; + DNSQuestion question; // Storage for any active question +}; + +extern ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *callbackInfo); +extern void CancelGetZoneData(mDNS *const m, ZoneData *nta); +extern mDNSBool IsGetZoneDataQuestion(DNSQuestion *q); + +typedef struct DNameListElem +{ + struct DNameListElem *next; + mDNSu32 uid; + domainname name; +} DNameListElem; + +#if APPLE_OSX_mDNSResponder +// Different states that we go through locating the peer +#define TC_STATE_AAAA_PEER 0x000000001 /* Peer's BTMM IPv6 address */ +#define TC_STATE_AAAA_PEER_RELAY 0x000000002 /* Peer's IPv6 Relay address */ +#define TC_STATE_SRV_PEER 0x000000003 /* Peer's SRV Record corresponding to IPv4 address */ +#define TC_STATE_ADDR_PEER 0x000000004 /* Peer's IPv4 address */ + +typedef struct ClientTunnel +{ + struct ClientTunnel *next; + domainname dstname; + mDNSBool MarkedForDeletion; + mDNSv6Addr loc_inner; + mDNSv4Addr loc_outer; + mDNSv6Addr loc_outer6; + mDNSv6Addr rmt_inner; + mDNSv4Addr rmt_outer; + mDNSv6Addr rmt_outer6; + mDNSIPPort rmt_outer_port; + mDNSu16 tc_state; + DNSQuestion q; +} ClientTunnel; +#endif // *************************************************************************** #if 0 -#pragma mark - NAT Traversal structures and constants +#pragma mark - +#pragma mark - NetworkInterfaceInfo_struct #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 struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; -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); +// 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 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; - }; +struct NetworkInterfaceInfo_struct +{ + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + NetworkInterfaceInfo *next; + + mDNSu8 InterfaceActive; // Set if interface is sending & receiving packets (see comment above) + mDNSu8 IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID + mDNSu8 IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID + + DNSQuestion NetWakeBrowse; + DNSQuestion NetWakeResolve[3]; // For fault-tolerance, we try up to three Sleep Proxies + mDNSAddr SPSAddr[3]; + mDNSIPPort SPSPort[3]; + mDNSs32 NextSPSAttempt; // -1 if we're not currently attempting to register with any Sleep Proxy + mDNSs32 NextSPSAttemptTime; + + // Standard AuthRecords that every Responder host should have (one per active IP address) + AuthRecord RR_A; // 'A' or 'AAAA' (address) record for our ".local" name + AuthRecord RR_PTR; // PTR (reverse lookup) record + AuthRecord RR_HINFO; + + // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() + mDNSInterfaceID InterfaceID; // Identifies physical interface; MUST NOT be 0, -1, or -2 + mDNSAddr ip; // The IPv4 or IPv6 address to advertise + mDNSAddr mask; + mDNSEthAddr MAC; + char ifname[64]; // Windows uses a GUID string for the interface name, which doesn't fit in 16 bytes + mDNSu8 Advertise; // False if you are only searching on this interface + mDNSu8 McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? + mDNSu8 NetWake; // Set if Wake-On-Magic-Packet is enabled on this interface + mDNSu8 Loopback; // Set if this is the loopback interface + mDNSu8 IgnoreIPv4LL; // Set if IPv4 Link-Local addresses have to be ignored. + mDNSu8 SendGoodbyes; // Send goodbyes on this interface while sleeping + mDNSBool DirectLink; // a direct link, indicating we can skip the probe for + // address records +}; + +#define SLE_DELETE 0x00000001 +#define SLE_WAB_BROWSE_QUERY_STARTED 0x00000002 +#define SLE_WAB_LBROWSE_QUERY_STARTED 0x00000004 +#define SLE_WAB_REG_QUERY_STARTED 0x00000008 + +typedef struct SearchListElem +{ + struct SearchListElem *next; + domainname domain; + int flag; + mDNSInterfaceID InterfaceID; + DNSQuestion BrowseQ; + DNSQuestion DefBrowseQ; + DNSQuestion AutomaticBrowseQ; + DNSQuestion RegisterQ; + DNSQuestion DefRegisterQ; + int numCfAnswers; + ARListElem *AuthRecs; +} SearchListElem; + +// For domain enumeration and automatic browsing +// This is the user's DNS search list. +// In each of these domains we search for our special pointer records (lb._dns-sd._udp.<domain>, etc.) +// to discover recommended domains for domain enumeration (browse, default browse, registration, +// default registration) and possibly one or more recommended automatic browsing domains. +extern SearchListElem *SearchList; // This really ought to be part of mDNS_struct -- SC // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Main mDNS object, used to hold all the mDNS state #endif -typedef void mDNSCallback(mDNS *const m, mStatus result); +typedef void mDNSCallback (mDNS *const m, mStatus result); +#ifndef CACHE_HASH_SLOTS #define CACHE_HASH_SLOTS 499 +#endif enum - { - mDNS_KnownBug_PhantomInterfaces = 1 - }; +{ + SleepState_Awake = 0, + SleepState_Transferring = 1, + SleepState_Sleeping = 2 +}; + +typedef enum +{ + kStatsActionIncrement, + kStatsActionDecrement, + kStatsActionClear, + kStatsActionSet +} DNSSECStatsAction; + +typedef enum +{ + kStatsTypeMemoryUsage, + kStatsTypeLatency, + kStatsTypeExtraPackets, + kStatsTypeStatus, + kStatsTypeProbe, + kStatsTypeMsgSize +} DNSSECStatsType; 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; +{ + mDNSu32 TotalMemUsed; + mDNSu32 Latency0; // 0 to 4 ms + mDNSu32 Latency5; // 5 to 9 ms + mDNSu32 Latency10; // 10 to 19 ms + mDNSu32 Latency20; // 20 to 49 ms + mDNSu32 Latency50; // 50 to 99 ms + mDNSu32 Latency100; // >= 100 ms + mDNSu32 ExtraPackets0; // 0 to 2 packets + mDNSu32 ExtraPackets3; // 3 to 6 packets + mDNSu32 ExtraPackets7; // 7 to 9 packets + mDNSu32 ExtraPackets10; // >= 10 packets + mDNSu32 SecureStatus; + mDNSu32 InsecureStatus; + mDNSu32 IndeterminateStatus; + mDNSu32 BogusStatus; + mDNSu32 NoResponseStatus; + mDNSu32 NumProbesSent; // Number of probes sent + mDNSu32 MsgSize0; // DNSSEC message size <= 1024 + mDNSu32 MsgSize1; // DNSSEC message size <= 2048 + mDNSu32 MsgSize2; // DNSSEC message size > 2048 +} DNSSECStatistics; + +typedef struct +{ + mDNSu32 NameConflicts; // Normal Name conflicts + mDNSu32 KnownUniqueNameConflicts; // Name Conflicts for KnownUnique Records + mDNSu32 DupQuerySuppressions; // Duplicate query suppressions + mDNSu32 KnownAnswerSuppressions; // Known Answer suppressions + mDNSu32 KnownAnswerMultiplePkts; // Known Answer in queries spannign multiple packets + mDNSu32 PoofCacheDeletions; // Number of times the cache was deleted due to POOF + mDNSu32 UnicastBitInQueries; // Queries with QU bit set + mDNSu32 NormalQueries; // Queries with QU bit not set + mDNSu32 MatchingAnswersForQueries; // Queries for which we had a response + mDNSu32 UnicastResponses; // Unicast responses to queries + mDNSu32 MulticastResponses; // Multicast responses to queries + mDNSu32 UnicastDemotedToMulticast; // Number of times unicast demoted to multicast + mDNSu32 Sleeps; // Total sleeps + mDNSu32 Wakes; // Total wakes + mDNSu32 InterfaceUp; // Total Interface UP events + mDNSu32 InterfaceUpFlap; // Total Interface UP events with flaps + mDNSu32 InterfaceDown; // Total Interface Down events + mDNSu32 InterfaceDownFlap; // Total Interface Down events with flaps + mDNSu32 CacheRefreshQueries; // Number of queries that we sent for refreshing cache + mDNSu32 CacheRefreshed; // Number of times the cache was refreshed due to a response + mDNSu32 WakeOnResolves; // Number of times we did a wake on resolve +} mDNSStatistics; +extern void LogMDNSStatistics(mDNS *const m); 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) +{ + // 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 + mDNSBool CanReceiveUnicastOn5353; + mDNSBool AdvertiseLocalAddresses; + mDNSBool DivertMulticastAdvertisements; // from interfaces that do not advertise local addresses to local-only + mStatus mDNSPlatformStatus; + mDNSIPPort UnicastPort4; + mDNSIPPort UnicastPort6; + mDNSEthAddr PrimaryMAC; // Used as unique host ID + mDNSCallback *MainCallback; + void *MainContext; + + // For debugging: To catch and report locking failures + mDNSu32 mDNS_busy; // Incremented between mDNS_Lock/mDNS_Unlock section + mDNSu32 mDNS_reentrancy; // Incremented when calling a client callback + mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified + mDNSu8 lock_Questions; + mDNSu8 lock_Records; +#ifndef MaxMsg + #define MaxMsg 512 +#endif + char MsgBuffer[MaxMsg]; // Temp storage used while building error log messages + + // Task Scheduling variables + mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards + mDNSs32 timenow; // The time that this particular activation of the mDNS code started + mDNSs32 timenow_last; // The time the last time we ran + mDNSs32 NextScheduledEvent; // Derived from values below + mDNSs32 ShutdownTime; // Set when we're shutting down; allows us to skip some unnecessary steps + mDNSs32 SuppressSending; // Don't send local-link mDNS packets during this time + mDNSs32 NextCacheCheck; // Next time to refresh cache record before it expires + mDNSs32 NextScheduledQuery; // Next time to send query in its exponential backoff sequence + mDNSs32 NextScheduledProbe; // Next time to probe for new authoritative record + mDNSs32 NextScheduledResponse; // Next time to send authoritative record(s) in responses + mDNSs32 NextScheduledNATOp; // Next time to send NAT-traversal packets + mDNSs32 NextScheduledSPS; // Next time to purge expiring Sleep Proxy records + mDNSs32 NextScheduledKA; // Next time to send Keepalive packets (SPS) + 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 + mDNSs32 MPktNum; // Unique sequence number assigned to each received Multicast packet + mDNSu8 LocalRemoveEvents; // Set if we may need to deliver remove events for local-only questions and/or local-only records + mDNSu8 SleepState; // Set if we're sleeping + mDNSu8 SleepSeqNum; // "Epoch number" of our current period of wakefulness + mDNSu8 SystemWakeOnLANEnabled; // Set if we want to register with a Sleep Proxy before going to sleep + mDNSu8 SentSleepProxyRegistration; // Set if we registered (or tried to register) with a Sleep Proxy + mDNSu8 SystemSleepOnlyIfWakeOnLAN; // Set if we may only sleep if we managed to register with a Sleep Proxy + mDNSs32 AnnounceOwner; // After waking from sleep, include OWNER option in packets until this time + mDNSs32 DelaySleep; // To inhibit re-sleeping too quickly right after wake + mDNSs32 SleepLimit; // Time window to allow deregistrations, etc., + // during which underying platform layer should inhibit system sleep + mDNSs32 TimeSlept; // Time we went to sleep. + + mDNSs32 StatStartTime; // Time we started gathering statistics during this interval. + mDNSs32 NextStatLogTime; // Next time to log statistics. + mDNSs32 ActiveStatTime; // Total time awake/gathering statistics for this log period. + mDNSs32 UnicastPacketsSent; // Number of unicast packets sent. + mDNSs32 MulticastPacketsSent; // Number of multicast packets sent. + mDNSs32 RemoteSubnet; // Multicast packets received from outside our subnet. + + mDNSs32 NextScheduledSPRetry; // Time next sleep proxy registration action is required. + // Only valid if SleepLimit is nonzero and DelaySleep is zero. + + mDNSs32 NextScheduledStopTime; // Next time to stop a question + + + // These fields only required for mDNS Searcher... + DNSQuestion *Questions; // List of all registered questions, active and inactive + DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache + DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() + DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly or mDNSInterface_P2P + DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only or P2P questions not yet answered + DNSQuestion *RestartQuestion; // Questions that are being restarted (stop followed by start) + DNSQuestion *ValidationQuestion; // Questions that are being validated (dnssec) + mDNSu32 rrcache_size; // Total number of available cache entries + mDNSu32 rrcache_totalused; // Number of cache entries currently occupied + mDNSu32 rrcache_totalused_unicast; // Number of cache entries currently occupied by unicast + mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions + mDNSu32 rrcache_report; + CacheEntity *rrcache_free; + CacheGroup *rrcache_hash[CACHE_HASH_SLOTS]; + mDNSs32 rrcache_nextcheck[CACHE_HASH_SLOTS]; + + AuthHash rrauth; + + // Fields below only required for mDNS Responder... + domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8 + domainlabel hostlabel; // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules + domainname MulticastHostname; // Fully Qualified "dot-local" Host Name, e.g. "Foo.local." + UTF8str255 HIHardware; + UTF8str255 HISoftware; + AuthRecord DeviceInfo; + AuthRecord *ResourceRecords; + AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records + AuthRecord *NewLocalRecords; // Fresh AuthRecords (public) not yet delivered to our local-only questions + AuthRecord *CurrentRecord; // Next AuthRecord about to be examined + mDNSBool NewLocalOnlyRecords; // Fresh AuthRecords (local only) not yet delivered to our local questions + NetworkInterfaceInfo *HostInterfaces; + mDNSs32 ProbeFailTime; + mDNSu32 NumFailedProbes; + mDNSs32 SuppressProbes; + Platform_t mDNS_plat; + + // Unicast-specific data + mDNSs32 NextuDNSEvent; // uDNS next event + mDNSs32 NextSRVUpdate; // Time to perform delayed update + + DNSServer *DNSServers; // list of DNS servers + McastResolver *McastResolvers; // list of Mcast Resolvers + + mDNSAddr Router; + mDNSAddr AdvertisedV4; // IPv4 address pointed to by hostname + mDNSAddr AdvertisedV6; // IPv6 address pointed to by hostname + + DomainAuthInfo *AuthInfoList; // list of domains requiring authentication for updates + + DNSQuestion ReverseMap; // Reverse-map query to find static hostname for service target + DNSQuestion AutomaticBrowseDomainQ; + domainname StaticHostname; // Current answer to reverse-map query + domainname FQDN; + HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata + NATTraversalInfo AutoTunnelNAT; // Shared between all AutoTunnel DomainAuthInfo structs + mDNSv6Addr AutoTunnelRelayAddr; + + mDNSu32 WABBrowseQueriesCount; // Number of WAB Browse domain enumeration queries (b, db) callers + mDNSu32 WABLBrowseQueriesCount; // Number of legacy WAB Browse domain enumeration queries (lb) callers + mDNSu32 WABRegQueriesCount; // Number of WAB Registration domain enumeration queries (r, dr) callers + mDNSu8 SearchDomainsHash[MD5_LEN]; + + // NAT-Traversal fields + NATTraversalInfo LLQNAT; // Single shared NAT Traversal to receive inbound LLQ notifications + NATTraversalInfo *NATTraversals; + NATTraversalInfo *CurrentNATTraversal; + mDNSs32 retryIntervalGetAddr; // delta between time sent and retry for NAT-PMP & UPnP/IGD external address request + mDNSs32 retryGetAddr; // absolute time when we retry for NAT-PMP & UPnP/IGD external address request + mDNSv4Addr ExtAddress; // the external address discovered via NAT-PMP or UPnP/IGD + mDNSu32 PCPNonce[3]; // the nonce if using PCP + + UDPSocket *NATMcastRecvskt; // For receiving PCP & NAT-PMP announcement multicasts from router on port 5350 + mDNSu32 LastNATupseconds; // NAT engine uptime in seconds, from most recent NAT packet + mDNSs32 LastNATReplyLocalTime; // Local time in ticks when most recent NAT packet was received + mDNSu16 LastNATMapResultCode; // Most recent error code for mappings + + tcpLNTInfo tcpAddrInfo; // legacy NAT traversal TCP connection info for external address + tcpLNTInfo tcpDeviceInfo; // legacy NAT traversal TCP connection info for device info + tcpLNTInfo *tcpInfoUnmapList; // list of pending unmap requests + mDNSInterfaceID UPnPInterfaceID; + UDPSocket *SSDPSocket; // For SSDP request/response + mDNSBool SSDPWANPPPConnection; // whether we should send the SSDP query for WANIPConnection or WANPPPConnection + mDNSIPPort UPnPRouterPort; // port we send discovery messages to + mDNSIPPort UPnPSOAPPort; // port we send SOAP messages to + mDNSu8 *UPnPRouterURL; // router's URL string + mDNSBool UPnPWANPPPConnection; // whether we're using WANIPConnection or WANPPPConnection + mDNSu8 *UPnPSOAPURL; // router's SOAP control URL string + mDNSu8 *UPnPRouterAddressString; // holds both the router's address and port + mDNSu8 *UPnPSOAPAddressString; // holds both address and port for SOAP messages + + // Sleep Proxy client fields + AuthRecord *SPSRRSet; // To help the client keep track of the records registered with the sleep proxy + + // Sleep Proxy Server fields + mDNSu8 SPSType; // 0 = off, 10-99 encodes desirability metric + mDNSu8 SPSPortability; // 10-99 + mDNSu8 SPSMarginalPower; // 10-99 + mDNSu8 SPSTotalPower; // 10-99 + mDNSu8 SPSFeatureFlags; // Features supported. Currently 1 = TCP KeepAlive supported. + mDNSu8 SPSState; // 0 = off, 1 = running, 2 = shutting down, 3 = suspended during sleep + mDNSInterfaceID SPSProxyListChanged; + UDPSocket *SPSSocket; +#ifndef SPC_DISABLED + ServiceRecordSet SPSRecords; +#endif + mDNSQuestionCallback *SPSBrowseCallback; // So the platform layer can do something useful with SPS browse results + int ProxyRecords; // Total number of records we're holding as proxy + #define MAX_PROXY_RECORDS 10000 /* DOS protection: 400 machines at 25 records each */ + +#if APPLE_OSX_mDNSResponder + ClientTunnel *TunnelClients; + uuid_t asl_uuid; // uuid for ASL logging + void *WCF; +#endif + // DNS Proxy fields + mDNSu32 dp_ipintf[MaxIp]; // input interface index list from the DNS Proxy Client + mDNSu32 dp_opintf; // output interface index from the DNS Proxy Client + + TrustAnchor *TrustAnchors; + int notifyToken; + int uds_listener_skt; // Listening socket for incoming UDS clients + mDNSBool mDNSOppCaching; // Opportunistic Caching + mDNSu32 AutoTargetServices; // # of services that have AutoTarget set + DNSSECStatistics DNSSECStats; + mDNSStatistics mDNSStats; + + // Fixed storage, to avoid creating large objects on the stack + // The imsg is declared as a union with a pointer type to enforce CPU-appropriate alignment + union { DNSMessage m; void *p; } imsg; // Incoming message received from wire + DNSMessage omsg; // Outgoing message we're building + LargeCacheRecord rec; // Resource Record extracted from received message +}; + +#define FORALL_CACHERECORDS(SLOT,CG,CR) \ + for ((SLOT) = 0; (SLOT) < CACHE_HASH_SLOTS; (SLOT)++) \ + for ((CG)=m->rrcache_hash[(SLOT)]; (CG); (CG)=(CG)->next) \ + for ((CR) = (CG)->members; (CR); (CR)=(CR)->next) // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Useful Static Constants #endif -extern const 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 mDNSInterfaceID mDNSInterface_Any; // Zero +extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value +extern const mDNSInterfaceID mDNSInterface_Unicast; // Special value +extern const mDNSInterfaceID mDNSInterfaceMark; // Special value +extern const mDNSInterfaceID mDNSInterface_P2P; // Special value +extern const mDNSInterfaceID uDNSInterfaceMark; // Special value + +extern const mDNSIPPort DiscardPort; +extern const mDNSIPPort SSHPort; +extern const mDNSIPPort UnicastDNSPort; +extern const mDNSIPPort SSDPPort; +extern const mDNSIPPort IPSECPort; +extern const mDNSIPPort NSIPCPort; +extern const mDNSIPPort NATPMPAnnouncementPort; +extern const mDNSIPPort NATPMPPort; +extern const mDNSIPPort DNSEXTPort; +extern const mDNSIPPort MulticastDNSPort; +extern const mDNSIPPort LoopbackIPCPort; +extern const mDNSIPPort PrivateDNSPort; + +extern const OwnerOptData zeroOwner; + +extern const mDNSIPPort zeroIPPort; +extern const mDNSv4Addr zerov4Addr; +extern const mDNSv6Addr zerov6Addr; +extern const mDNSEthAddr zeroEthAddr; +extern const mDNSv4Addr onesIPv4Addr; +extern const mDNSv6Addr onesIPv6Addr; +extern const mDNSEthAddr onesEthAddr; +extern const mDNSAddr zeroAddr; + +extern const mDNSv4Addr AllDNSAdminGroup; +extern const mDNSv4Addr AllHosts_v4; +extern const mDNSv6Addr AllHosts_v6; +extern const mDNSv6Addr NDP_prefix; +extern const mDNSEthAddr AllHosts_v6_Eth; +extern const mDNSAddr AllDNSLinkGroup_v4; +extern const mDNSAddr AllDNSLinkGroup_v6; extern const mDNSOpaque16 zeroID; +extern const mDNSOpaque16 onesID; extern const mDNSOpaque16 QueryFlags; extern const mDNSOpaque16 uQueryFlags; +extern const mDNSOpaque16 DNSSecQFlags; 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") - +extern const mDNSOpaque64 zeroOpaque64; + +extern mDNSBool StrictUnicastOrdering; +extern mDNSu8 NumUnicastDNSServers; + +#define localdomain (*(const domainname *)"\x5" "local") +#define DeviceInfoName (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp") +#define LocalDeviceInfoName (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp" "\x5" "local") +#define SleepProxyServiceType (*(const domainname *)"\xC" "_sleep-proxy" "\x4" "_udp") + // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Inline functions #endif #if (defined(_MSC_VER)) - #define mDNSinline static __inline + #define mDNSinline static __inline #elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) - #define mDNSinline static inline + #define mDNSinline static inline +#else + #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 @@ -2262,33 +2569,23 @@ extern mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v); #ifdef mDNSinline -mDNSinline mDNSs32 NonZeroTime(mDNSs32 t) { if (t) return(t); else return(1); } +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; - } +{ + mDNSOpaque16 x; + x.b[0] = (mDNSu8)(v >> 8); + x.b[1] = (mDNSu8)(v & 0xFF); + return(x); +} #endif // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Main Client Functions #endif @@ -2313,10 +2610,16 @@ mDNSinline mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v) // In principle, a proxy-like registration service could manually create address records for its own machine too, // but this would be pointless extra effort when using mDNS_Init_AdvertiseLocalAddresses does that for you. // +// Note that a client-only device that wishes to prohibit multicast advertisements (e.g. from +// higher-layer API calls) must also set DivertMulticastAdvertisements in the mDNS structure and +// advertise local address(es) on a loopback interface. +// // When mDNS has finished setting up the client's callback is called // A client can also spin and poll the mDNSPlatformStatus field to see when it changes from mStatus_Waiting to mStatus_NoError // -// Call mDNS_Close to tidy up before exiting +// Call mDNS_StartExit to tidy up before exiting +// Because exiting may be an asynchronous process (e.g. if unicast records need to be deregistered) +// client layer may choose to wait until mDNS_ExitNow() returns true before calling mDNS_FinalExit(). // // Call mDNS_Register with a completed AuthRecord object to register a resource record // If the resource record type is kDNSRecordTypeUnique (or kDNSknownunique) then if a conflicting resource record is discovered, @@ -2337,9 +2640,9 @@ mDNSinline mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v) // 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); + 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 @@ -2349,33 +2652,58 @@ extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, #define mDNS_Init_NoInitCallback mDNSNULL #define mDNS_Init_NoInitCallbackContext mDNSNULL +extern void mDNS_ConfigChanged(mDNS *const m); extern void mDNS_GrowCache (mDNS *const m, CacheEntity *storage, mDNSu32 numrecords); -extern void mDNS_Close (mDNS *const m); +extern void mDNS_GrowAuth (mDNS *const m, AuthEntity *storage, mDNSu32 numrecords); +extern void mDNS_StartExit (mDNS *const m); +extern void mDNS_FinalExit (mDNS *const m); +#define mDNS_Close(m) do { mDNS_StartExit(m); mDNS_FinalExit(m); } while(0) +#define mDNS_ExitNow(m, now) ((now) - (m)->ShutdownTime >= 0 || (!(m)->ResourceRecords)) + extern mDNSs32 mDNS_Execute (mDNS *const m); extern mStatus mDNS_Register (mDNS *const m, AuthRecord *const rr); extern mStatus mDNS_Update (mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, - const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback); + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback); extern mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr); extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question); extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const question); extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr); +extern mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval); extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr); +extern void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr); extern mDNSs32 mDNS_TimeNow(const mDNS *const m); +extern mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal); +extern mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal); +extern mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal); + +extern DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name); + +extern void mDNS_UpdateAllowSleep(mDNS *const m); + // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Platform support functions that are accessible to the client layer too #endif -extern mDNSs32 mDNSPlatformOneSecond; +extern mDNSs32 mDNSPlatformOneSecond; // *************************************************************************** #if 0 +#pragma mark - #pragma mark - General utility and helper functions #endif +// mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal +// mDNS_Dereg_rapid is used to send one goodbye instead of three, when we want the memory available for reuse sooner +// mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict +// mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered +typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type; + // mDNS_RegisterService is a single call to register the set of resource records associated with a given named service. // // mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery, @@ -2388,7 +2716,7 @@ extern mDNSs32 mDNSPlatformOneSecond; // 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. @@ -2399,53 +2727,77 @@ extern mDNSs32 mDNSPlatformOneSecond; // 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); + mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context); + +// mDNS_RegisterService() flags parameter bit definitions. +// Note these are only defined to transfer the corresponding DNSServiceFlags settings into mDNSCore routines, +// since code in mDNSCore does not include the DNSServiceFlags definitions in dns_sd.h. +enum +{ + coreFlagIncludeP2P = 0x1, // include P2P interfaces when using mDNSInterface_Any + coreFlagIncludeAWDL = 0x2, // include AWDL interface when using mDNSInterface_Any + coreFlagKnownUnique = 0x4, // client guarantees that SRV and TXT record names are unique + coreFlagWakeOnly = 0x8 // Service won't be registered with sleep proxy +}; 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); + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags); +extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 flags); 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_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt); +#define mDNS_DeregisterService(M,S) mDNS_DeregisterService_drt((M), (S), mDNS_Dereg_normal) extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, - const domainlabel *const name, const domainname *const type, const domainname *const domain, - const domainname *const host, - const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context); + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSu32 flags); #define mDNS_DeregisterNoSuchService mDNS_Deregister +extern void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name, + const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context); + extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, - const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context); + const domainname *const srv, const domainname *const domain, const mDNSu8 *anondata, + const mDNSInterfaceID InterfaceID, mDNSu32 flags, + mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, + 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; +{ + mDNS_DomainTypeBrowse = 0, + mDNS_DomainTypeBrowseDefault = 1, + mDNS_DomainTypeBrowseAutomatic = 2, + mDNS_DomainTypeRegistration = 3, + mDNS_DomainTypeRegistrationDefault = 4, + + mDNS_DomainTypeMax = 4 +} mDNS_DomainType; extern const char *const mDNS_DomainTypeNames[]; extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, - const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); #define mDNS_StopGetDomains mDNS_StopQuery extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname); #define mDNS_StopAdvertiseDomains mDNS_Deregister +extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m); +extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself); + +extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question); +extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question); + // *************************************************************************** #if 0 +#pragma mark - #pragma mark - DNS name utility functions #endif @@ -2458,16 +2810,29 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT // 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))) +#define AssignDomainName(DST, SRC) do { mDNSu16 len__ = DomainNameLength((SRC)); \ + if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__);else (DST)->c[0] = 0;} while(0) // Comparison functions +#define SameDomainLabelCS(A,B) ((A)[0] == (B)[0] && mDNSPlatformMemSame((A)+1, (B)+1, (A)[0])) extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); +extern mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2); +typedef mDNSBool DomainNameComparisonFn (const domainname *const d1, const domainname *const d2); extern mDNSBool IsLocalDomain(const domainname *d); // returns true for domains that by default should be looked up using link-local multicast +#define StripFirstLabel(X) ((const domainname *)& (X)->c[(X)->c[0] ? 1 + (X)->c[0] : 0]) + +#define FirstLabel(X) ((const domainlabel *)(X)) +#define SecondLabel(X) ((const domainlabel *)StripFirstLabel(X)) +#define ThirdLabel(X) ((const domainlabel *)StripFirstLabel(StripFirstLabel(X))) + +extern const mDNSu8 *LastLabel(const domainname *d); + // Get total length of domain name, in native DNS format, including terminal root label // (e.g. length of "com." is 5 (length byte, three data bytes, final zero) -extern mDNSu16 DomainNameLength(const domainname *const name); +extern mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit); +#define DomainNameLength(name) DomainNameLengthLimit((name), (name)->c + MAX_DOMAIN_NAME) // Append functions to append one or more labels to an existing native format domain name: // AppendLiteralLabelString adds a single label from a literal C string, with no escape character interpretation. @@ -2492,7 +2857,7 @@ extern mDNSu8 *MakeDomainNameFromDNSNameString (domainname *const name, const // 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. +// Similarly, when using ConvertDomainNameToCString, the target buffer must be MAX_ESCAPED_DOMAIN_NAME (1009) bytes long. // See definitions of MAX_ESCAPED_DOMAIN_LABEL and MAX_ESCAPED_DOMAIN_NAME for more detailed explanation. extern char *ConvertDomainLabelToCString_withescape(const domainlabel *const name, char *cstr, char esc); #define ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0) @@ -2519,6 +2884,7 @@ extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Other utility functions and macros #endif @@ -2531,87 +2897,99 @@ extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va 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); +extern char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer); #define RRDisplayString(m, rr) GetRRDisplayString_rdb(rr, &(rr)->rdata->u, (m)->MsgBuffer) #define ARDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) #define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); -extern mDNSBool IsPrivateV4Addr(mDNSAddr *addr); // returns true for RFC1918 private addresses +extern mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr); // returns true for RFC1918 private addresses +#define mDNSAddrIsRFC1918(X) ((X)->type == mDNSAddrType_IPv4 && mDNSv4AddrIsRFC1918(&(X)->ip.v4)) + +// For PCP +extern void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out); +extern mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr *out); + +#define mDNSSameIPPort(A,B) ((A).NotAnInteger == (B).NotAnInteger) +#define mDNSSameOpaque16(A,B) ((A).NotAnInteger == (B).NotAnInteger) +#define mDNSSameOpaque32(A,B) ((A).NotAnInteger == (B).NotAnInteger) +#define mDNSSameOpaque64(A,B) ((A)->l[0] == (B)->l[0] && (A)->l[1] == (B)->l[1]) #define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) #define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3]) +#define mDNSSameIPv6NetworkPart(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1]) #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 mDNSIPPortIsZero(A) ((A).NotAnInteger == 0) +#define mDNSOpaque16IsZero(A) ((A).NotAnInteger == 0) +#define mDNSOpaque64IsZero(A) (((A)->l[0] | (A)->l[1] ) == 0) +#define mDNSIPv4AddressIsZero(A) ((A).NotAnInteger == 0) +#define mDNSIPv6AddressIsZero(A) (((A).l[0] | (A).l[1] | (A).l[2] | (A).l[3]) == 0) +#define mDNSEthAddressIsZero(A) (((A).w[0] | (A).w[1] | (A).w[2] ) == 0) -#define mDNSIPv4AddressIsOnes(A) mDNSSameIPv4Address((A), onesIPv4Addr) -#define mDNSIPv6AddressIsOnes(A) mDNSSameIPv6Address((A), onesIPv6Addr) +#define mDNSIPv4AddressIsOnes(A) ((A).NotAnInteger == 0xFFFFFFFF) +#define mDNSIPv6AddressIsOnes(A) (((A).l[0] & (A).l[1] & (A).l[2] & (A).l[3]) == 0xFFFFFFFF) -#define mDNSAddressIsAllDNSLinkGroup(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroupv4)) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) +#define mDNSAddressIsAllDNSLinkGroup(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup_v4.ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroup_v6.ip.v6)) ) #define mDNSAddressIsZero(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero((X)->ip.v4)) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsZero((X)->ip.v6)) ) + ((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)) ) + ((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)) ) + ((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) + ((X)->type == mDNSAddrType_IPv4) ? !(mDNSIPv4AddressIsZero((X)->ip.v4) || mDNSIPv4AddressIsOnes((X)->ip.v4)) : \ + ((X)->type == mDNSAddrType_IPv6) ? !(mDNSIPv6AddressIsZero((X)->ip.v6) || mDNSIPv6AddressIsOnes((X)->ip.v6)) : mDNSfalse) + +#define mDNSv4AddressIsLinkLocal(X) ((X)->b[0] == 169 && (X)->b[1] == 254) +#define mDNSv6AddressIsLinkLocal(X) ((X)->b[0] == 0xFE && ((X)->b[1] & 0xC0) == 0x80) +#define mDNSAddressIsLinkLocal(X) ( \ + ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLinkLocal(&(X)->ip.v4) : \ + ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLinkLocal(&(X)->ip.v6) : mDNSfalse) + +#define mDNSv4AddressIsLoopback(X) ((X)->b[0] == 127 && (X)->b[1] == 0 && (X)->b[2] == 0 && (X)->b[3] == 1) +#define mDNSv6AddressIsLoopback(X) ((((X)->l[0] | (X)->l[1] | (X)->l[2]) == 0) && ((X)->b[12] == 0 && (X)->b[13] == 0 && (X)->b[14] == 0 && (X)->b[15] == 1)) + +#define mDNSAddressIsLoopback(X) ( \ + ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLoopback(&(X)->ip.v4) : \ + ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLoopback(&(X)->ip.v6) : mDNSfalse) // *************************************************************************** #if 0 +#pragma mark - #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) +// mDNS_SetSecretForDomain tells the core to authenticate (via TSIG with an HMAC_MD5 hash of the shared secret) // when dynamically updating a given zone (and its subdomains). The key used in authentication must be in // domain name format. The shared secret must be a null-terminated base64 encoded string. A minimum size of // 16 bytes (128 bits) is recommended for an MD5 hash as per RFC 2485. // Calling this routine multiple times for a zone replaces previously entered values. Call with a NULL key -// to dissable authentication for the zone. +// to disable authentication for the zone. A non-NULL autoTunnelPrefix means this is an AutoTunnel domain, +// and the value is prepended to the IPSec identifier (used for key lookup) + +extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, + const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel); -extern mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const char *sharedSecret); +extern void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks); // 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. @@ -2620,37 +2998,55 @@ extern mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const doma // 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. +// DNS servers used to resolve unicast queries are specified by mDNS_AddDNSServer. // For "split" DNS configurations, in which queries for different domains are sent to different servers (e.g. VPN and external), // a domain may be associated with a DNS server. For standard configurations, specify the root label (".") or NULL. - + extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext); extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn); extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router); -extern 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); +extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSs32 serviceID, const mDNSAddr *addr, + const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, + mDNSBool reqAAAA, mDNSBool reqDO); +extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags); +extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID); -// Routines called by the core, exported by DNSDigest.c +extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout); + +// We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2 +#define mDNS_AddSearchDomain_CString(X, I) \ + do { domainname d__; if (((X) != (void*)0) && MakeDomainNameFromDNSNameString(&d__, (X)) && d__.c[0]) mDNS_AddSearchDomain(&d__, I);} while(0) -// Convert a base64 encoded key into a binary byte stream -extern mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize); +// Routines called by the core, exported by DNSDigest.c -// 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); +// Convert an arbitrary base64 encoded key key into an HMAC key (stored in AuthInfo struct) +extern mDNSs32 DNSDigest_ConstructHMACKeyfromBase64(DomainAuthInfo *info, const char *b64key); -// sign a DNS message. The message must be compete, with all values in network byte order. end points to the end +// sign a DNS message. The message must be complete, with all values in network byte order. end points to the end // of the message, and is modified by this routine. numAdditionals is a pointer to the number of additional // records in HOST byte order, which is incremented upon successful completion of this routine. The function returns // the new end pointer on success, and NULL on failure. -extern mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info); +extern void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo *info, mDNSu16 tcode); + +#define SwapDNSHeaderBytes(M) do { \ + (M)->h.numQuestions = (mDNSu16)((mDNSu8 *)&(M)->h.numQuestions )[0] << 8 | ((mDNSu8 *)&(M)->h.numQuestions )[1]; \ + (M)->h.numAnswers = (mDNSu16)((mDNSu8 *)&(M)->h.numAnswers )[0] << 8 | ((mDNSu8 *)&(M)->h.numAnswers )[1]; \ + (M)->h.numAuthorities = (mDNSu16)((mDNSu8 *)&(M)->h.numAuthorities)[0] << 8 | ((mDNSu8 *)&(M)->h.numAuthorities)[1]; \ + (M)->h.numAdditionals = (mDNSu16)((mDNSu8 *)&(M)->h.numAdditionals)[0] << 8 | ((mDNSu8 *)&(M)->h.numAdditionals)[1]; \ +} while (0) + +#define DNSDigest_SignMessageHostByteOrder(M,E,INFO) \ + do { SwapDNSHeaderBytes(M); DNSDigest_SignMessage((M), (E), (INFO), 0); SwapDNSHeaderBytes(M); } while (0) + +// verify a DNS message. The message must be complete, with all values in network byte order. end points to the +// end of the record. tsig is a pointer to the resource record that contains the TSIG OPT record. info is +// the matching key to use for verifying the message. This function expects that the additionals member +// of the DNS message header has already had one subtracted from it. +extern mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCacheRecord *tsig, DomainAuthInfo *info, mDNSu16 *rcode, mDNSu16 *tcode); // *************************************************************************** #if 0 +#pragma mark - #pragma mark - PlatformSupport interface #endif @@ -2669,7 +3065,7 @@ extern mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *num // 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 +// NULL. InlineCacheRDSize is a compile-time constant, which is set by default to 68. If you need to handle records // a little larger than this and you don't want to have to implement run-time allocation and freeing, then you // can raise the value of this constant to a suitable value (at the expense of increased memory usage). // @@ -2686,29 +3082,65 @@ extern mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *num 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); + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstport, mDNSBool useBackgroundTrafficClass); +extern mDNSBool mDNSPlatformPeekUDP (mDNS *const m, UDPSocket *src); 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 mDNSPlatformStrCopy ( void *dst, const void *src); +extern mDNSu32 mDNSPlatformStrLen ( const void *src); +extern void mDNSPlatformMemCopy ( void *dst, const void *src, mDNSu32 len); +extern mDNSBool mDNSPlatformMemSame (const void *dst, const void *src, mDNSu32 len); +extern int mDNSPlatformMemCmp (const void *dst, const void *src, mDNSu32 len); +extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len); +extern void mDNSPlatformQsort (void *base, int nel, int width, int (*compar)(const void *, const void *)); +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING +#define mDNSPlatformMemAllocate(X) mallocL(# X, X) +#else extern void * mDNSPlatformMemAllocate (mDNSu32 len); +#endif extern void mDNSPlatformMemFree (void *mem); + +// If the platform doesn't have a strong PRNG, we define a naive multiply-and-add based on a seed +// from the platform layer. Long-term, we should embed an arc4 implementation, but the strength +// will still depend on the randomness of the seed. +#if !defined(_PLATFORM_HAS_STRONG_PRNG_) && (_BUILDING_XCODE_PROJECT_ || defined(_WIN32)) +#define _PLATFORM_HAS_STRONG_PRNG_ 1 +#endif +#if _PLATFORM_HAS_STRONG_PRNG_ +extern mDNSu32 mDNSPlatformRandomNumber(void); +#else extern mDNSu32 mDNSPlatformRandomSeed (void); +#endif // _PLATFORM_HAS_STRONG_PRNG_ + extern mStatus mDNSPlatformTimeInit (void); extern mDNSs32 mDNSPlatformRawTime (void); extern mDNSs32 mDNSPlatformUTC (void); -#define mDNS_TimeNow_NoLock(m) (mDNSPlatformRawTime() + m->timenow_adjust) +#define mDNS_TimeNow_NoLock(m) (mDNSPlatformRawTime() + (m)->timenow_adjust) + +#if MDNS_DEBUGMSGS +extern void mDNSPlatformWriteDebugMsg(const char *msg); +#endif +extern void mDNSPlatformWriteLogMsg(const char *ident, const char *msg, mDNSLogLevel_t loglevel); + +#if APPLE_OSX_mDNSResponder +// Utility function for ASL logging +mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...); + +// Log unicast and multicast traffic statistics once a day. Also used for DNSSEC statistics. +#define kDefaultNextStatsticsLogTime (24 * 60 * 60) + +extern void mDNSLogStatistics(mDNS *const m); + +#endif // APPLE_OSX_mDNSResponder // 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); +extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex); +extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange); // Every platform support module must provide the following functions if it is to support unicast DNS // and Dynamic Update. @@ -2722,46 +3154,71 @@ extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInte // 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. +// return the number of bytes read/written, 0 if the call would block, and -1 if an error. PlatformReadTCP +// should set the closed argument if the socket has been closed. // PlatformTCPCloseConnection must close the connection to the peer and remove the descriptor from the // event loop. CloseConnectin may be called at any time, including in a ConnectionCallback. -typedef 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); +typedef enum +{ + kTCPSocketFlags_Zero = 0, + kTCPSocketFlags_UseTLS = (1 << 0) +} TCPSocketFlags; + +typedef void (*TCPConnectionCallback)(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err); +extern TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass); // creates a TCP socket +extern TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd); +extern int mDNSPlatformTCPGetFD(TCPSocket *sock); +extern mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, + mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context); +extern void mDNSPlatformTCPCloseConnection(TCPSocket *sock); +extern long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed); +extern long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len); +extern UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport); +extern mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock); +extern void mDNSPlatformUDPClose(UDPSocket *sock); +extern void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd); +extern void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID); +extern void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID); +extern void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID); +extern void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst); +extern void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win); +extern mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti); +extern mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr); +extern mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname); +extern mStatus mDNSPlatformClearSPSMACAddr(void); + +// mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd +extern mStatus mDNSPlatformTLSSetupCerts(void); +extern void mDNSPlatformTLSTearDownCerts(void); // Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain // in browse/registration calls must implement these routines to get the "default" browse/registration list. -// 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 mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, + DNameListElem **BrowseDomains, mDNSBool ackConfig); +extern mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router); +extern void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status); -extern DNameListElem *mDNSPlatformGetSearchDomainList(void); -extern DNameListElem *mDNSPlatformGetRegDomainList(void); +extern void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason); +extern void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration); -// Helper functions provided by the core -extern DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig); -extern void mDNS_FreeDNameList(DNameListElem *list); +extern mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID); +extern mDNSBool mDNSPlatformInterfaceIsAWDL(const NetworkInterfaceInfo *intf); +extern mDNSBool mDNSPlatformValidRecordForQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); +extern mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf); +extern mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf); + +extern void mDNSPlatformFormatTime(unsigned long t, mDNSu8 *buf, int bufsize); #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); +extern void LNT_SendDiscoveryMsg(mDNS *m); +extern void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len); +extern mStatus LNT_GetExternalAddress(mDNS *m); +extern mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *const n); +extern mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *const n); +extern void LNT_ClearState(mDNS *const m); #endif // _LEGACY_NAT_TRAVERSAL_ // The core mDNS code provides these functions, for the platform support code to call at appropriate times @@ -2800,18 +3257,266 @@ extern mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp); // not lightweight second-by-second CPU power management modes.) extern void mDNS_SetFQDN(mDNS *const m); +extern void mDNS_ActivateNetWake_internal (mDNS *const m, NetworkInterfaceInfo *set); +extern void mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceInfo *set); extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping); extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping); extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); extern void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, - const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, + const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); +extern void mDNSCoreRestartQueries(mDNS *const m); +extern void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q); +extern void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount); +typedef void (*FlushCache)(mDNS *const m); +typedef void (*CallbackBeforeStartQuery)(mDNS *const m, void *context); +extern void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords, + CallbackBeforeStartQuery beforeQueryStart, void *context); +extern mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m); extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); +extern mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now); +extern mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now); + +extern void mDNSCoreReceiveRawPacket (mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID); extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); +extern CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay, mDNSBool Add, const mDNSAddr *sourceAddress); +extern CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name); +extern void ReleaseCacheRecord(mDNS *const m, CacheRecord *r); +extern void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event); +extern void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const rr); +extern void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease); +extern void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, + const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, + mDNSInterfaceID InterfaceID, DNSServer *dnsserver); +extern void CompleteDeregistration(mDNS *const m, AuthRecord *rr); +extern void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord); +extern void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr); +extern char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID); +extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *newServer); +extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr); +extern void CheckSuppressUnusableQuestions(mDNS *const m); +extern void RetrySearchDomainQuestions(mDNS *const m); +extern mDNSBool DomainEnumQuery(const domainname *qname); +extern mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr); +extern void UpdateKeepaliveRMACAsync(mDNS *const m, void *context); +extern void UpdateRMACCallback(mDNS *const m, void *context); + +// Used only in logging to restrict the number of /etc/hosts entries printed +extern void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result); +// exported for using the hash for /etc/hosts AuthRecords +extern AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name); +extern AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr); +extern AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr); +extern AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr); +extern mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype); + +// For now this AutoTunnel stuff is specific to Mac OS X. +// In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer +#if APPLE_OSX_mDNSResponder +extern void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); +extern void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q); +extern void StartServerTunnel(mDNS *const m, DomainAuthInfo *const info); +extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m); +extern void RemoveAutoTunnel6Record(mDNS *const m); +extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); +// For now this LocalSleepProxy stuff is specific to Mac OS X. +// In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer +extern mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf); +extern void mDNSPlatformUpdateDNSStatus(mDNS *const m, DNSQuestion *q); +extern void mDNSPlatformTriggerDNSRetry(mDNS *const m, DNSQuestion *v4q, DNSQuestion *v6q); +extern void mDNSPlatformLogToFile(int log_level, const char *buffer); +extern mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf); +#endif + +typedef void ProxyCallback (mDNS *const m, void *socket, void *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); +extern void mDNSPlatformInitDNSProxySkts(mDNS *const m, ProxyCallback *UDPCallback, ProxyCallback *TCPCallback); +extern void mDNSPlatformCloseDNSProxySkts(mDNS *const m); +extern void mDNSPlatformDisposeProxyContext(void *context); +extern mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *start, mDNSu8 *limit); + +// Sleep Assertions are specific to Mac OS X +#if APPLE_OSX_mDNSResponder +extern void mDNSPlatformSleepAssertion(mDNS *const m, double timeout); +#endif + +extern mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q); +extern mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q); +extern void mDNSPlatformSetuDNSSocktOpt(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q); +extern mDNSs32 mDNSPlatformGetPID(void); + +// *************************************************************************** +#if 0 +#pragma mark - +#pragma mark - Sleep Proxy +#endif + +// Sleep Proxy Server Property Encoding +// +// Sleep Proxy Servers are advertised using a structured service name, consisting of four +// metrics followed by a human-readable name. The metrics assist clients in deciding which +// Sleep Proxy Server(s) to use when multiple are available on the network. Each metric +// is a two-digit decimal number in the range 10-99. Lower metrics are generally better. +// +// AA-BB-CC-DD.FF Name +// +// Metrics: +// +// AA = Intent +// BB = Portability +// CC = Marginal Power +// DD = Total Power +// FF = Features Supported (Currently TCP Keepalive only) +// +// +// ** Intent Metric ** +// +// 20 = Dedicated Sleep Proxy Server -- a device, permanently powered on, +// installed for the express purpose of providing Sleep Proxy Service. +// +// 30 = Primary Network Infrastructure Hardware -- a router, DHCP server, NAT gateway, +// or similar permanently installed device which is permanently powered on. +// This is hardware designed for the express purpose of being network +// infrastructure, and for most home users is typically a single point +// of failure for the local network -- e.g. most home users only have +// a single NAT gateway / DHCP server. Even though in principle the +// hardware might technically be capable of running different software, +// a typical user is unlikely to do that. e.g. AirPort base station. +// +// 40 = Primary Network Infrastructure Software -- a general-purpose computer +// (e.g. Mac, Windows, Linux, etc.) which is currently running DHCP server +// or NAT gateway software, but the user could choose to turn that off +// fairly easily. e.g. iMac running Internet Sharing +// +// 50 = Secondary Network Infrastructure Hardware -- like primary infrastructure +// hardware, except not a single point of failure for the entire local network. +// For example, an AirPort base station in bridge mode. This may have clients +// associated with it, and if it goes away those clients will be inconvenienced, +// but unlike the NAT gateway / DHCP server, the entire local network is not +// dependent on it. +// +// 60 = Secondary Network Infrastructure Software -- like 50, but in a general- +// purpose CPU. +// +// 70 = Incidentally Available Hardware -- a device which has no power switch +// and is generally left powered on all the time. Even though it is not a +// part of what we conventionally consider network infrastructure (router, +// DHCP, NAT, DNS, etc.), and the rest of the network can operate fine +// without it, since it's available and unlikely to be turned off, it is a +// reasonable candidate for providing Sleep Proxy Service e.g. Apple TV, +// or an AirPort base station in client mode, associated with an existing +// wireless network (e.g. AirPort Express connected to a music system, or +// being used to share a USB printer). +// +// 80 = Incidentally Available Software -- a general-purpose computer which +// happens at this time to be set to "never sleep", and as such could be +// useful as a Sleep Proxy Server, but has not been intentionally provided +// for this purpose. Of all the Intent Metric categories this is the +// one most likely to be shut down or put to sleep without warning. +// However, if nothing else is availalable, it may be better than nothing. +// e.g. Office computer in the workplace which has been set to "never sleep" +// +// +// ** Portability Metric ** +// +// Inversely related to mass of device, on the basis that, all other things +// being equal, heavier devices are less likely to be moved than lighter devices. +// E.g. A MacBook running Internet Sharing is probably more likely to be +// put to sleep and taken away than a Mac Pro running Internet Sharing. +// The Portability Metric is a logarithmic decibel scale, computed by taking the +// (approximate) mass of the device in milligrammes, taking the base 10 logarithm +// of that, multiplying by 10, and subtracting the result from 100: +// +// Portability Metric = 100 - (log10(mg) * 10) +// +// The Portability Metric is not necessarily computed literally from the actual +// mass of the device; the intent is just that lower numbers indicate more +// permanent devices, and higher numbers indicate devices more likely to be +// removed from the network, e.g., in order of increasing portability: +// +// Mac Pro < iMac < Laptop < iPhone +// +// Example values: +// +// 10 = 1 metric tonne +// 40 = 1kg +// 70 = 1g +// 90 = 10mg +// +// +// ** Marginal Power and Total Power Metrics ** +// +// The Marginal Power Metric is the power difference between sleeping and staying awake +// to be a Sleep Proxy Server. +// +// The Total Power Metric is the total power consumption when being Sleep Proxy Server. +// +// The Power Metrics use a logarithmic decibel scale, computed as ten times the +// base 10 logarithm of the (approximate) power in microwatts: +// +// Power Metric = log10(uW) * 10 +// +// Higher values indicate higher power consumption. Example values: +// +// 10 = 10 uW +// 20 = 100 uW +// 30 = 1 mW +// 60 = 1 W +// 90 = 1 kW + +typedef enum +{ + mDNSSleepProxyMetric_Dedicated = 20, + mDNSSleepProxyMetric_PrimaryHardware = 30, + mDNSSleepProxyMetric_PrimarySoftware = 40, + mDNSSleepProxyMetric_SecondaryHardware = 50, + mDNSSleepProxyMetric_SecondarySoftware = 60, + mDNSSleepProxyMetric_IncidentalHardware = 70, + mDNSSleepProxyMetric_IncidentalSoftware = 80 +} mDNSSleepProxyMetric; + +typedef enum +{ + mDNS_NoWake = 0, // System does not support Wake on LAN + mDNS_WakeOnAC = 1, // System supports Wake on LAN when connected to AC power only + mDNS_WakeOnBattery = 2 // System supports Wake on LAN on battery +} mDNSWakeForNetworkAccess; + +extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features); +#define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP,F) \ + do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP),(F)); mDNS_Unlock(m); } while(0) + +extern void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const CacheRecord *sps[3]); +#define PrototypeSPSName(X) ((X)[0] >= 11 && (X)[3] == '-' && (X)[ 4] == '9' && (X)[ 5] == '9' && \ + (X)[6] == '-' && (X)[ 7] == '9' && (X)[ 8] == '9' && \ + (X)[9] == '-' && (X)[10] == '9' && (X)[11] == '9' ) +#define ValidSPSName(X) ((X)[0] >= 5 && mDNSIsDigit((X)[1]) && mDNSIsDigit((X)[2]) && mDNSIsDigit((X)[4]) && mDNSIsDigit((X)[5])) +#define SPSMetric(X) (!ValidSPSName(X) || PrototypeSPSName(X) ? 1000000 : \ + ((X)[1]-'0') * 100000 + ((X)[2]-'0') * 10000 + ((X)[4]-'0') * 1000 + ((X)[5]-'0') * 100 + ((X)[7]-'0') * 10 + ((X)[8]-'0')) +#define LocalSPSMetric(X) ( (X)->SPSType * 10000 + (X)->SPSPortability * 100 + (X)->SPSMarginalPower) +#define SPSFeatures(X) ((X)[0] >= 13 && (X)[12] =='.' ? ((X)[13]-'0') : 0 ) + +#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; + +extern int MD5_Init(MD5_CTX *c); +extern int MD5_Update(MD5_CTX *c, const void *data, unsigned long len); +extern int MD5_Final(unsigned char *md, MD5_CTX *c); + // *************************************************************************** #if 0 +#pragma mark - #pragma mark - Compile-Time assertion checks #endif @@ -2821,30 +3526,72 @@ extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); // 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]; - }; +struct CompileTimeAssertionChecks_mDNS +{ + // Check that the compiler generated our on-the-wire packet format structure definitions + // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. + char assert0[(sizeof(rdataSRV) == 262 ) ? 1 : -1]; + char assert1[(sizeof(DNSMessageHeader) == 12 ) ? 1 : -1]; + char assert2[(sizeof(DNSMessage) == 12+AbsoluteMaxDNSMessageData) ? 1 : -1]; + char assert3[(sizeof(mDNSs8) == 1 ) ? 1 : -1]; + char assert4[(sizeof(mDNSu8) == 1 ) ? 1 : -1]; + char assert5[(sizeof(mDNSs16) == 2 ) ? 1 : -1]; + char assert6[(sizeof(mDNSu16) == 2 ) ? 1 : -1]; + char assert7[(sizeof(mDNSs32) == 4 ) ? 1 : -1]; + char assert8[(sizeof(mDNSu32) == 4 ) ? 1 : -1]; + char assert9[(sizeof(mDNSOpaque16) == 2 ) ? 1 : -1]; + char assertA[(sizeof(mDNSOpaque32) == 4 ) ? 1 : -1]; + char assertB[(sizeof(mDNSOpaque128) == 16 ) ? 1 : -1]; + char assertC[(sizeof(CacheRecord ) == sizeof(CacheGroup) ) ? 1 : -1]; + char assertD[(sizeof(int) >= 4 ) ? 1 : -1]; + char assertE[(StandardAuthRDSize >= 256 ) ? 1 : -1]; + char assertF[(sizeof(EthernetHeader) == 14 ) ? 1 : -1]; + char assertG[(sizeof(ARP_EthIP ) == 28 ) ? 1 : -1]; + char assertH[(sizeof(IPv4Header ) == 20 ) ? 1 : -1]; + char assertI[(sizeof(IPv6Header ) == 40 ) ? 1 : -1]; + char assertJ[(sizeof(IPv6NDP ) == 24 ) ? 1 : -1]; + char assertK[(sizeof(UDPHeader ) == 8 ) ? 1 : -1]; + char assertL[(sizeof(IKEHeader ) == 28 ) ? 1 : -1]; + char assertM[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; + + // Check our structures are reasonable sizes. Including overly-large buffers, or embedding + // other overly-large structures instead of having a pointer to them, can inadvertently + // cause structure sizes (and therefore memory usage) to balloon unreasonably. + char sizecheck_RDataBody [(sizeof(RDataBody) == 264) ? 1 : -1]; + char sizecheck_ResourceRecord [(sizeof(ResourceRecord) <= 72) ? 1 : -1]; + char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1]; + char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 232) ? 1 : -1]; + char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 232) ? 1 : -1]; + char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 832) ? 1 : -1]; + +// Checks commented out when sizeof(DNSQuestion) change cascaded into having to change yet another +// set of hardcoded size values because these structures contain one or more DNSQuestion +// instances. +// char sizecheck_ZoneData [(sizeof(ZoneData) <= 1648) ? 1 : -1]; + char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 200) ? 1 : -1]; + char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; + char sizecheck_DNSServer [(sizeof(DNSServer) <= 340) ? 1 : -1]; +// char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6988) ? 1 : -1]; + char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5540) ? 1 : -1]; + char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7888) ? 1 : -1]; +// char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3302) ? 1 : -1]; +#if APPLE_OSX_mDNSResponder +// char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1160) ? 1 : -1]; +#endif +}; + +// Routine to initialize device-info TXT record contents +mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr); + +#if APPLE_OSX_mDNSResponder +extern void D2D_start_advertising_interface(NetworkInterfaceInfo *interface); +extern void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface); +#endif // *************************************************************************** #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 index 933078dab0..5a385e6299 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c @@ -5,295 +5,23 @@ * 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 "DNSCommon.h" +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include "dns_sd.h" +#include "dnssec.h" +#include "nsec.h" #include <assert.h> #include <stdio.h> @@ -328,30 +56,32 @@ First checkin // *************************************************************************** // Structures -// We keep a list of client-supplied event sources in PosixEventSource records +// 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; +{ + 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; +{ + 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 +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 + +static PosixNetworkInterface *gRecentInterfaces; // *************************************************************************** // Globals (for debugging) @@ -368,39 +98,51 @@ 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; - } +{ + 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; + case AF_INET6: + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; #ifndef NOT_HAVE_SA_LEN - assert(sin6->sin6_len == sizeof(*sin6)); + 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; - } + 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; - } - } + default: + verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family); + ipAddr->type = mDNSAddrType_None; + if (ipPort) ipPort->NotAnInteger = 0; + break; + } +} + +/* + * Apple source is using this to set mobile platform + * specific options. + */ +/*ARGSUSED*/ +mDNSexport void mDNSPlatformSetuDNSSocktOpt(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) +{ + (void)src; /* unused */ + (void)dst; /* unused */ + (void)q; /* unused */ +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Send and Receive @@ -408,226 +150,341 @@ mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipA // 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; + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass) +{ + int err = 0; + struct sockaddr_storage to; + PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID); + int sendingsocket = -1; + + (void)src; // Will need to use this parameter once we implement mDNSPlatformUDPSocket/mDNSPlatformUDPClose + (void) useBackgroundTrafficClass; + + assert(m != NULL); + assert(msg != NULL); + assert(end != NULL); + assert((((char *) end) - ((char *) msg)) > 0); + + if (dstPort.NotAnInteger == 0) + { + LogMsg("mDNSPlatformSendUDP: Invalid argument -dstPort is set to 0"); + return PosixErrorToStatus(EINVAL); + } + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin = (struct sockaddr_in*)&to; #ifndef NOT_HAVE_SA_LEN - sin->sin_len = sizeof(*sin); + 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; - } + 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)); + 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); + 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; - } + 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 (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; + 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); - } + if (!mDNSAddressIsAllDNSLinkGroup(dst)) + if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); + + /* dont report ENETUNREACH */ + if (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); - } +{ + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort; + ssize_t packetLen; + DNSMessage packet; + struct my_in_pktinfo packetInfo; + struct sockaddr_storage from; + socklen_t fromLen; + int flags; + mDNSu8 ttl; + mDNSBool reject; + const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL; + + assert(m != NULL); + assert(skt >= 0); + + fromLen = sizeof(from); + flags = 0; + packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl); + + if (packetLen >= 0) + { + SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); + SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); + + // If we have broken IP_RECVDSTADDR functionality (so far + // I've only seen this on OpenBSD) then apply a hack to + // convince mDNS Core that this isn't a spoof packet. + // Basically what we do is check to see whether the + // packet arrived as a multicast and, if so, set its + // destAddr to the mDNS address. + // + // I must admit that I could just be doing something + // wrong on OpenBSD and hence triggering this problem + // but I'm at a loss as to how. + // + // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have + // no way to tell the destination address or interface this packet arrived on, + // so all we can do is just assume it's a multicast + + #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) + if ((destAddr.NotAnInteger == 0) && (flags & MSG_MCAST)) + { + destAddr.type = senderAddr.type; + if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4; + else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroup_v6.ip.v6; + } + #endif + + // We only accept the packet if the interface on which it came + // in matches the interface associated with this socket. + // We do this match by name or by index, depending on which + // information is available. recvfrom_flags sets the name + // to "" if the name isn't available, or the index to -1 + // if the index is available. This accomodates the various + // different capabilities of our target platforms. + + reject = mDNSfalse; + if (!intf) + { + // Ignore multicasts accidentally delivered to our unicast receiving socket + if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1; + } + else + { + if (packetInfo.ipi_ifname[0] != 0) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0); + else if (packetInfo.ipi_ifindex != -1) reject = (packetInfo.ipi_ifindex != intf->index); + + if (reject) + { + verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d", + &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, + &intf->coreIntf.ip, intf->intfName, intf->index, skt); + packetLen = -1; + num_pkts_rejected++; + if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2) + { + fprintf(stderr, + "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n", + num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected); + num_pkts_accepted = 0; + num_pkts_rejected = 0; + } + } + else + { + verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d", + &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt); + num_pkts_accepted++; + } + } + } + + if (packetLen >= 0) + mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, + &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); +} + +mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src) +{ + (void)m; // unused + (void)src; // unused + return mDNSfalse; +} + +mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass) +{ + (void)m; // Unused + (void)flags; // Unused + (void)port; // Unused + (void)useBackgroundTrafficClass; // Unused + return NULL; +} + +mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd) +{ + (void)flags; // Unused + (void)sd; // Unused + return NULL; +} + +mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) +{ + (void)sock; // Unused + return -1; +} + +mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context) +{ + (void)sock; // Unused + (void)dst; // Unused + (void)dstport; // Unused + (void)hostname; // Unused + (void)InterfaceID; // Unused + (void)callback; // Unused + (void)context; // Unused + return(mStatus_UnsupportedErr); +} + +mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) +{ + (void)sock; // Unused +} + +mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool * closed) +{ + (void)sock; // Unused + (void)buf; // Unused + (void)buflen; // Unused + (void)closed; // Unused + return 0; +} + +mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) +{ + (void)sock; // Unused + (void)msg; // Unused + (void)len; // Unused + return 0; +} + +mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS * const m, mDNSIPPort port) +{ + (void)m; // Unused + (void)port; // Unused + return NULL; +} + +mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) +{ + (void)sock; // Unused +} + +mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) +{ + (void)m; // Unused + (void)InterfaceID; // Unused +} + +mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) +{ + (void)msg; // Unused + (void)end; // Unused + (void)InterfaceID; // Unused +} + +mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) +{ + (void)m; // Unused + (void)tpa; // Unused + (void)tha; // Unused + (void)InterfaceID; // Unused +} + +mDNSexport mStatus mDNSPlatformTLSSetupCerts(void) +{ + return(mStatus_UnsupportedErr); +} + +mDNSexport void mDNSPlatformTLSTearDownCerts(void) +{ +} + +mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) +{ + (void) m; + (void) allowSleep; + (void) reason; +} #if COMPILER_LIKES_PRAGMA_MARK -#pragma mark ***** Get/Free Search Domain List +#pragma mark - +#pragma mark - /etc/hosts support #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; - } +mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + (void)m; // unused + (void)rr; + (void)result; +} + + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** DDNS Config Platform Functions +#endif + +mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, + DNameListElem **BrowseDomains, mDNSBool ackConfig) +{ + (void) m; + (void) setservers; + (void) fqdn; + (void) setsearch; + (void) RegDomains; + (void) BrowseDomains; + (void) ackConfig; + + return mDNStrue; +} + +mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router) +{ + (void) m; + (void) v4; + (void) v6; + (void) router; + + return mStatus_UnsupportedErr; +} + +mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status) +{ + (void) dname; + (void) status; +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Init and Term @@ -635,825 +492,888 @@ mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void) // 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; - } +{ + 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); - } +{ + // 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++; - } - } - fclose(fp); - return (numOfServers > 0) ? 0 : -1; - } +{ + char line[256]; + char nameserver[16]; + char keyword[11]; + int numOfServers = 0; + FILE *fp = fopen(filePath, "r"); + if (fp == NULL) return -1; + while (fgets(line,sizeof(line),fp)) + { + struct in_addr ina; + line[255]='\0'; // just to be safe + if (sscanf(line,"%10s %15s", keyword, nameserver) != 2) continue; // it will skip whitespaces + if (strncasecmp(keyword,"nameserver",10)) continue; + if (inet_aton(nameserver, (struct in_addr *)&ina) != 0) + { + mDNSAddr DNSAddr; + DNSAddr.type = mDNSAddrType_IPv4; + DNSAddr.ip.v4.NotAnInteger = ina.s_addr; + mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, 0, &DNSAddr, UnicastDNSPort, kScopeNone, 0, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse); + numOfServers++; + } + } + fclose(fp); + 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; +{ + PosixNetworkInterface *intf; - assert(m != NULL); - assert(intfName != NULL); + assert(m != NULL); + assert(intfName != NULL); - intf = (PosixNetworkInterface*)(m->HostInterfaces); - while ( (intf != NULL) && (strcmp(intf->intfName, intfName) != 0) ) - intf = (PosixNetworkInterface *)(intf->coreIntf.next); + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ((intf != NULL) && (strcmp(intf->intfName, intfName) != 0)) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); - return intf; - } + return intf; +} mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 index) - { - PosixNetworkInterface *intf; +{ + PosixNetworkInterface *intf; + + assert(m != NULL); + + if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); + if (index == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); + if (index == kDNSServiceInterfaceIndexAny ) return(mDNSInterface_Any); - assert(m != NULL); + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ((intf != NULL) && (mDNSu32) intf->index != index) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); - if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); + return (mDNSInterfaceID) intf; +} - intf = (PosixNetworkInterface*)(m->HostInterfaces); - while ( (intf != NULL) && (mDNSu32) intf->index != index) - intf = (PosixNetworkInterface *)(intf->coreIntf.next); +mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange) +{ + PosixNetworkInterface *intf; + (void) suppressNetworkChange; // Unused - return (mDNSInterfaceID) intf; - } - -mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id) - { - PosixNetworkInterface *intf; + assert(m != NULL); - assert(m != NULL); + if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); + if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); + if (id == mDNSInterface_Any ) return(kDNSServiceInterfaceIndexAny); - if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ((intf != NULL) && (mDNSInterfaceID) intf != id) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); - intf = (PosixNetworkInterface*)(m->HostInterfaces); - while ( (intf != NULL) && (mDNSInterfaceID) intf != id) - intf = (PosixNetworkInterface *)(intf->coreIntf.next); + if (intf) return intf->index; - return intf ? intf->index : 0; - } + // If we didn't find the interface, check the RecentInterfaces list as well + intf = gRecentInterfaces; + 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); +{ + 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); + if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0); #endif - free(intf); - } + + // Move interface to the RecentInterfaces list for a minute + intf->LastSeen = mDNSPlatformUTC(); + intf->coreIntf.next = &gRecentInterfaces->coreIntf; + gRecentInterfaces = 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; - } +{ + 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); +{ + 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); + 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) + else return EINVAL; + + if (*sktPtr < 0) { err = errno; perror((intfAddr->sa_family == AF_INET) ? "socket AF_INET" : "socket AF_INET6"); } + + // ... with a shared UDP port, if it's for multicast receiving + if (err == 0 && port.NotAnInteger) + { + #if defined(SO_REUSEPORT) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); + #elif defined(SO_REUSEADDR) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + #else + #error This platform has no way to avoid address busy errors on multicast. + #endif + if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } + + // Enable inbound packets on IFEF_AWDL interface. + // Only done for multicast sockets, since we don't expect unicast socket operations + // on the IFEF_AWDL interface. Operation is a no-op for other interface types. + #ifdef SO_RECV_ANYIF + if (setsockopt(*sktPtr, SOL_SOCKET, SO_RECV_ANYIF, &kOn, sizeof(kOn)) < 0) perror("setsockopt - SO_RECV_ANYIF"); + #endif + } + + // We want to receive destination addresses and interface identifiers. + if (intfAddr->sa_family == AF_INET) + { + struct ip_mreq imr; + struct sockaddr_in bindAddr; + if (err == 0) + { + #if defined(IP_PKTINFO) // Linux + err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } + #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris + #if defined(IP_RECVDSTADDR) + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } + #endif + #if defined(IP_RECVIF) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } + } + #endif + #else + #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts + #endif + } + #if defined(IP_RECVTTL) // Linux + if (err == 0) + { + setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn)); + // We no longer depend on being able to get the received TTL, so don't worry if the option fails + } + #endif + + // Add multicast group membership on this interface + if (err == 0 && JoinMulticastGroup) + { + imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; + imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; + err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); } + } + + // Specify outgoing interface too + if (err == 0 && JoinMulticastGroup) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr)); + if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); } + } + + // Per the mDNS spec, send unicast packets with TTL 255 + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); } + } + + // and multicast packets with TTL 255 too + // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); + if (err < 0 && errno == EINVAL) + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } + } + + // And start listening for packets + if (err == 0) + { + bindAddr.sin_family = AF_INET; + bindAddr.sin_port = port.NotAnInteger; + bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket + err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); + if (err < 0) { err = errno; perror("bind"); fflush(stderr); } + } + } // endif (intfAddr->sa_family == AF_INET) #if HAVE_IPV6 - else if (intfAddr->sa_family == AF_INET6) - { - struct ipv6_mreq imr6; - struct sockaddr_in6 bindAddr6; - #if defined(IPV6_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)); + 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_RECVPKTINFO"); } + } + #elif defined(IPV6_PKTINFO) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_PKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } + } + #else + #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts + #endif + #if defined(IPV6_RECVHOPLIMIT) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_RECVHOPLIMIT"); } + } + #elif defined(IPV6_HOPLIMIT) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_HOPLIMIT, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); } + } + #endif + + // Add multicast group membership on this interface + if (err == 0 && JoinMulticastGroup) + { + imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6; + imr6.ipv6mr_interface = interfaceIndex; + //LogMsg("Joining %.16a on %d", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6)); + if (err < 0) + { + err = errno; + verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); + perror("setsockopt - IPV6_JOIN_GROUP"); + } + } + + // Specify outgoing interface too + if (err == 0 && JoinMulticastGroup) + { + u_int multicast_if = interfaceIndex; + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); } + } + + // We want to receive only IPv6 packets on this socket. + // Without this option, we may get IPv4 addresses as mapped addresses. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); } + } + + // Per the mDNS spec, send unicast packets with TTL 255 + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); } + } + + // and multicast packets with TTL 255 too + // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); + if (err < 0 && errno == EINVAL) + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } + } + + // And start listening for packets + if (err == 0) + { + mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6)); #ifndef NOT_HAVE_SA_LEN - bindAddr6.sin6_len = sizeof(bindAddr6); + 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) + 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; - } + // 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; +{ + 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; + 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); + 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); + 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; - } + } + + // If interface is a direct link, address record will be marked as kDNSRecordTypeKnownUnique + // and skip the probe phase of the probe/announce packet sequence. + intf->coreIntf.DirectLink = mDNSfalse; +#ifdef DIRECTLINK_INTERFACE_NAME + if (strcmp(intfName, STRINGIFY(DIRECTLINK_INTERFACE_NAME)) == 0) + intf->coreIntf.DirectLink = mDNStrue; +#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; +{ + 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"); + assert(m != NULL); + debugf("SetupInterfaceList"); - if (intfList == NULL) err = ENOENT; + 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); - } + 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 (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) + || (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; - } + ) && (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); + + // Clean up any interfaces that have been hanging around on the RecentInterfaces list for more than a minute + PosixNetworkInterface **ri = &gRecentInterfaces; + const mDNSs32 utc = mDNSPlatformUTC(); + while (*ri) + { + PosixNetworkInterface *pi = *ri; + if (utc - pi->LastSeen < 60) ri = (PosixNetworkInterface **)&pi->coreIntf.next; + else { *ri = (PosixNetworkInterface *)pi->coreIntf.next; free(pi); } + } + + 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; - } +mDNSlocal mStatus OpenIfNotifySocket(int *pFD) +{ + mStatus err = mStatus_NoError; + struct sockaddr_nl snl; + int sock; + int ret; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + return errno; + + // Configure read to be non-blocking because inbound msg size is not known in advance + (void) fcntl(sock, F_SETFL, O_NONBLOCK); + + /* Subscribe the socket to Link & IP addr notifications. */ + mDNSPlatformMemZero(&snl, sizeof snl); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; + ret = bind(sock, (struct sockaddr *) &snl, sizeof snl); + if (0 == ret) + *pFD = sock; + else + err = errno; + + return err; +} #if MDNS_DEBUGMSGS -mDNSlocal void PrintNetLinkMsg( const struct nlmsghdr *pNLMsg) - { - const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" }; - const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" }; - - printf( "nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len, - pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[ pNLMsg->nlmsg_type] : kNLRtMsgTypes[ pNLMsg->nlmsg_type - RTM_BASE], - pNLMsg->nlmsg_flags); - - if ( RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK) - { - struct ifinfomsg *pIfInfo = (struct ifinfomsg*) NLMSG_DATA( pNLMsg); - printf( "ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family, - pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change); - - } - else if ( RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR) - { - struct ifaddrmsg *pIfAddr = (struct ifaddrmsg*) NLMSG_DATA( pNLMsg); - printf( "ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family, - pIfAddr->ifa_index, pIfAddr->ifa_flags); - } - printf( "\n"); - } +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) +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 - } +{ + 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); + 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; - } + // 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); +mDNSlocal mStatus OpenIfNotifySocket(int *pFD) +{ + *pFD = socket(AF_ROUTE, SOCK_RAW, 0); - if ( *pFD < 0) - return mStatus_UnknownErr; + 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); + // 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; - } + 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" }; +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; + 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); - } + printf("ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[pRSMsg->ifam_type], index); +} #endif -mDNSlocal mDNSu32 ProcessRoutingNotification( int sd) +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; +{ + 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 + 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); + 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; - } + // Process the message + switch (pRSMsg->ifam_type) + { + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_IFINFO: + if (pRSMsg->ifam_type == RTM_IFINFO) + result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index; + else + result |= 1 << pRSMsg->ifam_index; + break; + /* + * ADD & DELETE are happening when IPv6 announces are changing, + * and for some reason it will stop mdnsd to announce IPv6 + * addresses. So we force mdnsd to check interfaces. + */ + case RTM_ADD: + case RTM_DELETE: + result |= 1; + } + + 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); - } +mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context) +{ + IfChangeRec *pChgRec = (IfChangeRec*) context; + fd_set readFDs; + mDNSu32 changedInterfaces = 0; + struct timeval zeroTimeout = { 0, 0 }; + + (void)fd; // Unused + (void)filter; // Unused + + FD_ZERO(&readFDs); + FD_SET(pChgRec->NotifySD, &readFDs); + + do + { + changedInterfaces |= ProcessRoutingNotification(pChgRec->NotifySD); + } + while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)); + + // Currently we rebuild the entire interface list whenever any interface change is + // detected. If this ever proves to be a performance issue in a multi-homed + // configuration, more care should be paid to changedInterfaces. + if (changedInterfaces) + mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS); +} // Register with either a Routing Socket or RtNetLink to listen for interface changes. mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m) - { - mStatus err; - IfChangeRec *pChgRec; +{ + mStatus err; + IfChangeRec *pChgRec; - pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate( sizeof *pChgRec); - if ( pChgRec == NULL) - return mStatus_NoMemoryErr; + 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); + pChgRec->mDNS = m; + err = OpenIfNotifySocket(&pChgRec->NotifySD); + if (err == 0) + err = mDNSPosixAddFDToEventLoop(pChgRec->NotifySD, InterfaceChangeCallback, pChgRec); - return err; - } + 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); - } +{ + 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); +{ + int err = 0; + struct sockaddr sa; + assert(m != NULL); - if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; + if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; - // Tell mDNS core the names of this machine. + // 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 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"); + // 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); + mDNS_SetFQDN(m); - sa.sa_family = AF_INET; - m->p->unicastSocket4 = -1; - if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4); + 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); + 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); - } + // Tell mDNS core about the network interfaces on this machine. + if (err == mStatus_NoError) err = SetupInterfaceList(m); + + // Tell mDNS core about DNS Servers + mDNS_Lock(m); + if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE); + mDNS_Unlock(m); + + if (err == mStatus_NoError) + { + err = WatchForInterfaceChange(m); + // Failure to observe interface changes is non-fatal. + if (err != mStatus_NoError) + { + fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", (int)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); +{ + 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); + if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0); #endif - } +} +// This is used internally by InterfaceChangeCallback. +// It's also exported so that the Standalone Responder (mDNSResponderPosix) +// can call it in response to a SIGHUP (mainly for debugging purposes). mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) - { - int err; - ClearInterfaceList(m); - err = SetupInterfaceList(m); - return PosixErrorToStatus(err); - } +{ + int err; + // This is a pretty heavyweight way to process interface changes -- + // destroying the entire interface list and then making fresh one from scratch. + // We should make it like the OS X version, which leaves unchanged interfaces alone. + ClearInterfaceList(m); + err = SetupInterfaceList(m); + return PosixErrorToStatus(err); +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Locking @@ -1465,16 +1385,16 @@ mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) // 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 - } +{ + (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 - } +{ + (void) m; // Unused +} #if COMPILER_LIKES_PRAGMA_MARK #pragma mark ***** Strings @@ -1482,291 +1402,468 @@ mDNSexport void mDNSPlatformUnlock (const mDNS *const m) // 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); - } +mDNSexport void mDNSPlatformStrCopy(void *dst, const void *src) +{ + 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); - } +{ + 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); - } +mDNSexport void mDNSPlatformMemCopy(void *dst, const void *src, mDNSu32 len) +{ + memcpy(dst, src, len); +} // mDNS core calls this routine to test whether blocks of memory are byte-for-byte // identical. On the Posix platform this is a simple wrapper around ANSI C memcmp. -mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) - { - return memcmp(dst, src, len) == 0; - } +mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len) +{ + return memcmp(dst, src, len) == 0; +} + +// If the caller wants to know the exact return of memcmp, then use this instead +// of mDNSPlatformMemSame +mDNSexport int mDNSPlatformMemCmp(const void *dst, const void *src, mDNSu32 len) +{ + return (memcmp(dst, src, len)); +} + +mDNSexport void mDNSPlatformQsort(void *base, int nel, int width, int (*compar)(const void *, const void *)) +{ + (void)qsort(base, nel, width, compar); +} + +// DNSSEC stub functions +mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) +{ + (void)m; + (void)dv; + (void)q; +} + +mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode) +{ + (void)m; + (void)crlist; + (void)negcr; + (void)rcode; + return mDNSfalse; +} + +mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) +{ + (void)m; + (void)action; + (void)type; + (void)value; +} + +// Proxy stub functions +mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit) +{ + (void) q; + (void) h; + (void) msg; + (void) ptr; + (void) limit; + + return ptr; +} + +mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[], mDNSu32 OpIf) +{ + (void) m; + (void) IpIfArr; + (void) OpIf; +} + +mDNSexport void DNSProxyTerminate(mDNS *const m) +{ + (void) m; +} // 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 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); - } +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return(tv.tv_usec); +} -mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024; +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); - } +{ + // 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) ); - } +{ + 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); - } +{ + return time(NULL); +} + +mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration) +{ + (void) m; + (void) InterfaceID; + (void) EthAddr; + (void) IPAddr; + (void) iteration; +} + +mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) +{ + (void) rr; + (void) intf; + + return 1; +} + +mDNSexport mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf) +{ + (void) q; + (void) intf; + + return 1; +} + +// Used for debugging purposes. For now, just set the buffer to zero +mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsize) +{ + (void) te; + if (bufsize) buf[0] = 0; +} + +mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win) +{ + (void) sadd; // Unused + (void) dadd; // Unused + (void) lport; // Unused + (void) rport; // Unused + (void) seq; // Unused + (void) ack; // Unused + (void) win; // Unused +} + +mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) +{ + (void) m; // Unused + (void) laddr; // Unused + (void) raddr; // Unused + (void) lport; // Unused + (void) rport; // Unused + (void) mti; // Unused + + return mStatus_NoError; +} + +mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr) +{ + (void) raddr; // Unused + (void) m; // Unused + + return mStatus_NoError; +} + +mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) +{ + (void) spsaddr; // Unused + (void) ifname; // Unused + + return mStatus_NoError; +} + +mDNSexport mStatus mDNSPlatformClearSPSMACAddr(void) +{ + return mStatus_NoError; +} + +mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) +{ + (void) sock; // unused + + return (mDNSu16)-1; +} + +mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) +{ + (void) InterfaceID; // unused + + return mDNSfalse; +} + +mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) +{ + (void) m; + (void) q; + return mDNStrue; +} + +mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q) +{ + (void) m; + (void) q; + return -1; +} + +mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) +{ + (void) src; + (void) dst; + (void) q; +} + +mDNSexport mDNSs32 mDNSPlatformGetPID() +{ + return 0; +} mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s) - { - if (*nfds < s + 1) *nfds = s + 1; - FD_SET(s, readfds); - } +{ + 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; +{ + mDNSs32 ticks; + struct timeval interval; - // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do - mDNSs32 nextevent = mDNS_Execute(m); + // 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); + // 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); + if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6); #endif - while (info) - { - if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); + while (info) + { + if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); #if HAVE_IPV6 - if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); + if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); #endif - info = (PosixNetworkInterface *)(info->coreIntf.next); - } + 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 + // 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; - } + // 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); - } +{ + 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); - } + 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); - } + 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); - } + 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); - } - } + 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; - } +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)); +mStatus mDNSPosixAddFDToEventLoop(int fd, mDNSPosixEventCallback callback, void *context) +{ + PosixEventSource *newSource; - if ( fd >= (int) FD_SETSIZE || fd < 0) - return mStatus_UnsupportedErr; - if ( callback == NULL) - return mStatus_BadParamErr; + if (gEventSources.LinkOffset == 0) + InitLinkedList(&gEventSources, offsetof(PosixEventSource, Next)); - newSource = (PosixEventSource*) malloc( sizeof *newSource); - if ( NULL == newSource) - return mStatus_NoMemoryErr; + if (fd >= (int) FD_SETSIZE || fd < 0) + return mStatus_UnsupportedErr; + if (callback == NULL) + return mStatus_BadParamErr; - newSource->Callback = callback; - newSource->Context = context; - newSource->fd = fd; + newSource = (PosixEventSource*) malloc(sizeof *newSource); + if (NULL == newSource) + return mStatus_NoMemoryErr; - AddToTail( &gEventSources, newSource); - FD_SET( fd, &gEventFDs); + newSource->Callback = callback; + newSource->Context = context; + newSource->fd = fd; - DetermineMaxEventFD(); + AddToTail(&gEventSources, newSource); + FD_SET(fd, &gEventFDs); - return mStatus_NoError; - } + 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; - } +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); - } +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; +mStatus mDNSPosixListenForSignalInEventLoop(int signum) +{ + struct sigaction action; + mStatus err; + + mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment + action.sa_handler = NoteSignal; + err = sigaction(signum, &action, (struct sigaction*) NULL); - bzero( &action, sizeof action); // more portable than member-wise assignment - action.sa_handler = NoteSignal; - err = sigaction( signum, &action, (struct sigaction*) NULL); - - sigaddset( &gEventSignalSet, signum); + sigaddset(&gEventSignalSet, signum); - return err; - } + return err; +} // Tell the event package to stop listening for signal in mDNSPosixRunEventLoopOnce(). -mStatus mDNSPosixIgnoreSignalInEventLoop( int signum) - { - struct sigaction action; - mStatus err; +mStatus mDNSPosixIgnoreSignalInEventLoop(int signum) +{ + struct sigaction action; + mStatus err; + + mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment + action.sa_handler = SIG_DFL; + err = sigaction(signum, &action, (struct sigaction*) NULL); - 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); + sigdelset(&gEventSignalSet, signum); - return err; - } + 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; - } +mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout, + sigset_t *pSignalsReceived, mDNSBool *pDataDispatched) +{ + fd_set listenFDs = gEventFDs; + int fdMax = 0, numReady; + struct timeval timeout = *pTimeout; + + // Include the sockets that are listening to the wire in our select() set + mDNSPosixGetFDSet(m, &fdMax, &listenFDs, &timeout); // timeout may get modified + if (fdMax < gMaxFD) + fdMax = gMaxFD; + + numReady = select(fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout); + + // If any data appeared, invoke its callback + if (numReady > 0) + { + PosixEventSource *iSource; + + (void) mDNSPosixProcessFDSet(m, &listenFDs); // call this first to process wire data for clients + + for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + { + if (FD_ISSET(iSource->fd, &listenFDs)) + { + iSource->Callback(iSource->fd, 0, iSource->Context); + break; // in case callback removed elements from gEventSources + } + } + *pDataDispatched = mDNStrue; + } + else + *pDataDispatched = mDNSfalse; + + (void) sigprocmask(SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL); + *pSignalsReceived = gEventSignals; + sigemptyset(&gEventSignals); + (void) sigprocmask(SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL); + + return mStatus_NoError; +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h index bd5286c899..d3d413eb3c 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h @@ -5,77 +5,15 @@ * 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 @@ -84,7 +22,7 @@ First checkin #include <sys/time.h> #ifdef __cplusplus - extern "C" { +extern "C" { #endif // PosixNetworkInterface is a record extension of the core NetworkInterfaceInfo @@ -96,32 +34,33 @@ First checkin typedef struct PosixNetworkInterface PosixNetworkInterface; struct PosixNetworkInterface - { - NetworkInterfaceInfo coreIntf; - const char * intfName; - PosixNetworkInterface * aliasIntf; - int index; - int multicastSocket4; +{ + NetworkInterfaceInfo coreIntf; // MUST be the first element in this structure + mDNSs32 LastSeen; + const char * intfName; + PosixNetworkInterface * aliasIntf; + int index; + int multicastSocket4; #if HAVE_IPV6 - int multicastSocket6; + 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; +{ + int unicastSocket4; #if HAVE_IPV6 - int unicastSocket6; + 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. +// 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. @@ -132,7 +71,7 @@ extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m); 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); +typedef void (*mDNSPosixEventCallback)(int fd, short filter, void *context); extern mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context); extern mStatus mDNSPosixRemoveFDFromEventLoop( int fd); @@ -141,7 +80,7 @@ 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 index fbb4267417..026eaae565 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c @@ -1,7 +1,3 @@ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ /* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. @@ -9,9 +5,9 @@ * 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. @@ -21,14 +17,13 @@ #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 <signal.h> #include <unistd.h> #include <stdio.h> @@ -36,24 +31,24 @@ 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 +/* 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, +/* 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> @@ -64,138 +59,161 @@ #include <net/if_var.h> #else #include <alloca.h> -#endif /* !HAVE_SOLARIS */ +#endif /* !HAVE_SOLARIS */ #include <netinet/in_var.h> -// NOTE: netinet/in_var.h implicitly includes netinet6/in6_var.h for us +// Note: netinet/in_var.h implicitly includes netinet6/in6_var.h for us #endif -#if defined(AF_INET6) && HAVE_IPV6 - -#if HAVE_LINUX +#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX #include <netdb.h> #include <arpa/inet.h> /* Converts a prefix length to IPv6 network mask */ void plen_to_mask(int plen, char *addr) { - int i; - int colons=7; /* Number of colons in IPv6 address */ - int bits_in_block=16; /* Bits per IPv6 block */ - for(i=0;i<=colons;i++) { - int block, ones=0xffff, ones_in_block; - if(plen>bits_in_block) ones_in_block=bits_in_block; - else ones_in_block=plen; - block = ones & (ones << (bits_in_block-ones_in_block)); - i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block); - plen -= ones_in_block; - } - } + 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 */ - } +{ + struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr; + FILE *fp; + char addr[8][5]; + int flags, myflags, index, plen, scope; + char ifname[9], lastname[IFNAMSIZ]; + char addr6[32+7+1]; /* don't forget the seven ':' */ + struct addrinfo hints, *res0; + struct sockaddr_in6 *sin6; + struct in6_addr *addrptr; + int err; + int sockfd = -1; + struct ifreq ifr; + + res0=NULL; + ifihead = NULL; + ifipnext = &ifihead; + lastname[0] = 0; -#endif /* LINUX */ -#endif /* defined(AF_INET6) && HAVE_IPV6 */ + if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) { + sockfd = socket(AF_INET6, SOCK_DGRAM, 0); + if (sockfd < 0) { + goto gotError; + } + while (fscanf(fp, + "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %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; + } + + ifipold = *ifipnext; /* need this later */ + ifiptr = ifipnext; + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ + + sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", + addr[0],addr[1],addr[2],addr[3], + addr[4],addr[5],addr[6],addr[7]); + + /* Add address of the interface */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + err = getaddrinfo(addr6, NULL, &hints, &res0); + if (err) { + goto gotError; + } + ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6)); + + /* Add netmask of the interface */ + char ipv6addr[INET6_ADDRSTRLEN]; + plen_to_mask(plen, ipv6addr); + ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + sin6=calloc(1, sizeof(struct sockaddr_in6)); + addrptr=calloc(1, sizeof(struct in6_addr)); + inet_pton(family, ipv6addr, addrptr); + sin6->sin6_family=family; + sin6->sin6_addr=*addrptr; + sin6->sin6_scope_id=scope; + memcpy(ifi->ifi_netmask, sin6, sizeof(struct sockaddr_in6)); + free(sin6); + + + /* Add interface name */ + memcpy(ifi->ifi_name, ifname, IFI_NAME); + + /* Add interface index */ + ifi->ifi_index = index; + + /* Add interface flags*/ + memcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + if (errno == EADDRNOTAVAIL) { + /* + * If the main interface is configured with no IP address but + * an alias interface exists with an IP address, you get + * EADDRNOTAVAIL for the main interface + */ + free(ifi->ifi_addr); + free(ifi); + ifipnext = ifiptr; + *ifipnext = ifipold; + continue; + } else { + goto gotError; + } + } + ifi->ifi_flags = ifr.ifr_flags; + freeaddrinfo(res0); + res0=NULL; + } + } + goto done; + +gotError: + if (ifihead != NULL) { + free_ifi_info(ifihead); + ifihead = NULL; + } + if (res0 != NULL) { + freeaddrinfo(res0); + res0=NULL; + } +done: + if (sockfd != -1) { + assert(close(sockfd) == 0); + } + return(ifihead); /* pointer to first structure in linked list */ +} +#endif // defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX #if HAVE_SOLARIS @@ -230,7 +248,7 @@ select_src_ifi_info_solaris(int sockfd, int numifs, char *chptr; char cmpifname[LIFNAMSIZ]; int i; - uint64_t best_lifrflags; + uint64_t best_lifrflags = 0; uint64_t ifflags; *best_lifr = NULL; @@ -509,23 +527,23 @@ gotError: 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; + int junk; + struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr; + int sockfd, sockf6, len, lastlen, flags, myflags; #ifdef NOT_HAVE_IF_NAMETOINDEX - int index = 200; + int index = 200; #endif char *ptr, *buf, lastname[IFNAMSIZ], *cptr; - struct ifconf ifc; + 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); + if (family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); #elif HAVE_SOLARIS return get_ifi_info_solaris(family); #endif @@ -534,7 +552,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) sockf6 = -1; buf = NULL; ifihead = NULL; - + sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { goto gotError; @@ -577,7 +595,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) 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 */ @@ -595,7 +613,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) { goto gotError; } - + flags = ifrcopy.ifr_flags; if ((flags & IFF_UP) == 0) continue; /* ignore if interface not up */ @@ -604,8 +622,10 @@ struct ifi_info *get_ifi_info(int family, int doaliases) if (ifi == NULL) { goto gotError; } - *ifipnext = ifi; /* prev points to this new one */ - ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ + ifipold = *ifipnext; /* need this later */ + ifiptr = ifipnext; + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ ifi->ifi_flags = flags; /* IFF_xxx values */ ifi->ifi_myflags = myflags; /* IFI_xxx values */ @@ -618,7 +638,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) ifi->ifi_index = ifrcopy.ifr_index; else #endif - ifi->ifi_index = index++; /* SIOCGIFINDEX is broken on Solaris 2.5ish, so fake it */ + 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'; @@ -635,16 +655,32 @@ struct ifi_info *get_ifi_info(int family, int doaliases) 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 */ + if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) { + if (errno == EADDRNOTAVAIL) { + /* + * If the main interface is configured with no IP address but + * an alias interface exists with an IP address, you get + * EADDRNOTAVAIL for the main interface + */ + free(ifi->ifi_addr); + free(ifi); + ifipnext = ifiptr; + *ifipnext = ifipold; + continue; + } else { + goto gotError; + } + } + + ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_netmask == NULL) goto gotError; + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_addr; + /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ #ifndef NOT_HAVE_SA_LEN - sinptr->sin_len = sizeof(struct sockaddr_in); + sinptr->sin_len = sizeof(struct sockaddr_in); #endif - sinptr->sin_family = AF_INET; - memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); + sinptr->sin_family = AF_INET; + memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); #endif #ifdef SIOCGIFBRDADDR @@ -653,11 +689,11 @@ struct ifi_info *get_ifi_info(int family, int doaliases) 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 */ + /* 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 ); + sinptr->sin_len = sizeof( struct sockaddr_in ); #endif - sinptr->sin_family = AF_INET; + sinptr->sin_family = AF_INET; ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_brdaddr == NULL) { goto gotError; @@ -674,9 +710,9 @@ struct ifi_info *get_ifi_info(int family, int doaliases) 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 ); + sinptr->sin_len = sizeof( struct sockaddr_in ); #endif - sinptr->sin_family = AF_INET; + sinptr->sin_family = AF_INET; ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_dstaddr == NULL) { goto gotError; @@ -695,27 +731,42 @@ struct ifi_info *get_ifi_info(int family, int doaliases) 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; + 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)); - } + { + struct in6_ifreq ifr6; + if (sockf6 == -1) + sockf6 = socket(AF_INET6, SOCK_DGRAM, 0); + memset(&ifr6, 0, sizeof(ifr6)); + memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name )); + memcpy(&ifr6.ifr_ifru.ifru_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_ifru.ifru_addr)); + if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) { + if (errno == EADDRNOTAVAIL) { + /* + * If the main interface is configured with no IP address but + * an alias interface exists with an IP address, you get + * EADDRNOTAVAIL for the main interface + */ + free(ifi->ifi_addr); + free(ifi); + ifipnext = ifiptr; + *ifipnext = ifipold; + continue; + } else { + goto gotError; + } + } + ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_netmask == NULL) goto gotError; + sinptr6 = (struct sockaddr_in6 *) &ifr6.ifr_ifru.ifru_addr; + memcpy(ifi->ifi_netmask, sinptr6, sizeof(struct sockaddr_in6)); + } #endif } break; @@ -726,7 +777,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) } } goto done; - + gotError: if (ifihead != NULL) { free_ifi_info(ifihead); @@ -758,35 +809,35 @@ free_ifi_info(struct ifi_info *ifihead) for (ifi = ifihead; ifi != NULL; ifi = ifinext) { if (ifi->ifi_addr != NULL) free(ifi->ifi_addr); + if (ifi->ifi_netmask != NULL) + free(ifi->ifi_netmask); if (ifi->ifi_brdaddr != NULL) free(ifi->ifi_brdaddr); if (ifi->ifi_dstaddr != NULL) free(ifi->ifi_dstaddr); - 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 +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; + 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 */ + 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 + *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); @@ -808,12 +859,12 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, *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 + /* 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; } @@ -821,7 +872,7 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, /* include recvfrom_flags2 */ #ifndef CMSG_FIRSTHDR - #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc. + #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc. *flagsp = 0; /* pass back results */ return(n); #else @@ -836,18 +887,18 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, #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; -}; + struct in_pktinfo + { + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; + }; #endif - if (cmptr->cmsg_level == IPPROTO_IP && + 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; @@ -861,7 +912,7 @@ struct in_pktinfo 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; @@ -877,7 +928,16 @@ struct in_pktinfo 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)); + /* + * the is memcpy used for sparc? no idea;) + * pktp->ipi_ifindex = sdl->sdl_index; + */ + (void) memcpy(&pktp->ipi_ifindex, CMSG_DATA(cmptr), sizeof(uint_t)); +#ifdef HAVE_BROKEN_RECVIF_NAME + if (sdl->sdl_index == 0) { + pktp->ipi_ifindex = *(uint_t*)sdl; + } +#endif assert(pktp->ipi_ifname[IFI_NAME - 1] == 0); // null terminated because of memset above continue; @@ -887,22 +947,22 @@ struct in_pktinfo #ifdef IP_RECVTTL if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVTTL) { - *ttl = *(u_char*)CMSG_DATA(cmptr); + *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); + 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) { + 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); - + 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); @@ -911,15 +971,15 @@ struct in_pktinfo sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = 0; sin6->sin6_port = 0; - pktp->ipi_ifindex = ip6_info->ipi6_ifindex; + pktp->ipi_ifindex = ip6_info->ipi6_ifindex; continue; } #endif #if defined(IPV6_HOPLIMIT) && HAVE_IPV6 - if (cmptr->cmsg_level == IPPROTO_IPV6 && + if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == IPV6_HOPLIMIT) { - *ttl = *(int*)CMSG_DATA(cmptr); + *ttl = *(int*)CMSG_DATA(cmptr); continue; } #endif @@ -937,44 +997,44 @@ struct in_pktinfo #ifdef NOT_HAVE_DAEMON #include <fcntl.h> #include <sys/stat.h> -#include <signal.h> +#include <sys/signal.h> int daemon(int nochdir, int noclose) +{ + switch (fork()) + { + case -1: return (-1); // Fork failed + case 0: break; // Child -- continue + default: _exit(0); // Parent -- exit + } + + if (setsid() == -1) return(-1); + + signal(SIGHUP, SIG_IGN); + + switch (fork()) // Fork again, primarily for reasons of Unix trivia { - 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); + 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 index 68594d219d..cc81b7d393 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h @@ -5,83 +5,15 @@ * 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 @@ -90,18 +22,26 @@ First checkin #include <sys/socket.h> #include <net/if.h> #include <netinet/in.h> -#include <arpa/inet.h> #ifdef HAVE_LINUX #include <linux/socket.h> +#define IPV6_2292_PKTINFO IPV6_2292PKTINFO +#define IPV6_2292_HOPLIMIT IPV6_2292HOPLIMIT +#else +// The following are the supported non-linux posix OSes - +// netbsd, freebsd and openbsd. +#if HAVE_IPV6 +#define IPV6_2292_PKTINFO 19 +#define IPV6_2292_HOPLIMIT 20 +#endif #endif #ifdef __cplusplus - extern "C" { +extern "C" { #endif #ifdef NOT_HAVE_SOCKLEN_T - typedef unsigned int socklen_t; +typedef unsigned int socklen_t; #endif #if !defined(_SS_MAXSIZE) @@ -109,7 +49,7 @@ First checkin #define sockaddr_storage sockaddr_in6 #else #define sockaddr_storage sockaddr -#endif // HAVE_IPV6 +#endif // HAVE_IPV6 #endif // !defined(_SS_MAXSIZE) #ifndef NOT_HAVE_SA_LEN @@ -129,8 +69,8 @@ First checkin struct my_in_pktinfo { struct sockaddr_storage ipi_addr; - int ipi_ifindex; /* received interface index */ - char ipi_ifname[IFI_NAME]; /* received interface name */ + int ipi_ifindex; /* received interface index */ + char ipi_ifname[IFI_NAME]; /* received interface name */ }; /* From the text (Stevens, section 20.2): */ @@ -140,33 +80,33 @@ struct my_in_pktinfo { /* 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 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 */ + 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): */ @@ -184,7 +124,7 @@ extern int daemon(int nochdir, int noclose); #endif #ifdef __cplusplus - } +} #endif #endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/nsec.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/nsec.h new file mode 100644 index 0000000000..3dbb841f15 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/nsec.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __NSEC_H +#define __NSEC_H + +#include "dnssec.h" + +extern mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode); +extern void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv); +extern void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *rr); +extern mDNSBool NSECAnswersDS(mDNS *const m, ResourceRecord *rr, DNSQuestion *q); +extern int CountLabelsMatch(const domainname *const d1, const domainname *const d2); +extern void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); +extern void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, + DNSSECVerifierCallback callback); +extern CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype); +extern void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); + +#endif // __NSEC_H diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c index ec2bf87479..f3206a49e1 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c @@ -1,5253 +1,6023 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 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): + * To Do: + * Elimate all mDNSPlatformMemAllocate/mDNSPlatformMemFree from this code -- the core code + * is supposed to be malloc-free so that it runs in constant memory determined at compile-time. + * Any dynamic run-time requirements should be handled by the platform layer below or client layer above + */ -$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 +#if APPLE_OSX_mDNSResponder +#include <TargetConditionals.h> +#endif +#include "uDNS.h" -Revision 1.230 2006/06/29 03:02:44 cheshire -<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support +#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 -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 +// For domain enumeration and automatic browsing +// This is the user's DNS search list. +// In each of these domains we search for our special pointer records (lb._dns-sd._udp.<domain>, etc.) +// to discover recommended domains for domain enumeration (browse, default browse, registration, +// default registration) and possibly one or more recommended automatic browsing domains. +mDNSexport SearchListElem *SearchList = mDNSNULL; -Revision 1.228 2006/02/26 00:54:42 cheshire -Fixes to avoid code generation warning/error on FreeBSD 7 +// The value can be set to true by the Platform code e.g., MacOSX uses the plist mechanism +mDNSBool StrictUnicastOrdering = mDNSfalse; -Revision 1.227 2006/01/09 20:47:05 cheshire -<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use" +// We keep track of the number of unicast DNS servers and log a message when we exceed 64. +// Currently the unicast queries maintain a 64 bit map to track the valid DNS servers for that +// question. Bit position is the index into the DNS server list. This is done so to try all +// the servers exactly once before giving up. If we could allocate memory in the core, then +// arbitrary limitation of 64 DNSServers can be removed. +mDNSu8 NumUnicastDNSServers = 0; +#define MAX_UNICAST_DNS_SERVERS 64 -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 +#define SetNextuDNSEvent(m, rr) { \ + if ((m)->NextuDNSEvent - ((rr)->LastAPTime + (rr)->ThisAPInterval) >= 0) \ + (m)->NextuDNSEvent = ((rr)->LastAPTime + (rr)->ThisAPInterval); \ +} -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 +#ifndef UNICAST_DISABLED -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 +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - General Utility Functions +#endif -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(). +// set retry timestamp for record with exponential backoff +mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random) +{ + rr->LastAPTime = m->timenow; -Revision 1.222 2005/10/05 23:04:10 cheshire -Add more information to unlinkAR and startLLQHandshakeCallback error messages + if (rr->expire && rr->refreshCount < MAX_UPDATE_REFRESH_COUNT) + { + mDNSs32 remaining = rr->expire - m->timenow; + rr->refreshCount++; + if (remaining > MIN_UPDATE_REFRESH_TIME) + { + // Refresh at 70% + random (currently it is 0 to 10%) + rr->ThisAPInterval = 7 * (remaining/10) + (random ? random : mDNSRandom(remaining/10)); + // Don't update more often than 5 minutes + if (rr->ThisAPInterval < MIN_UPDATE_REFRESH_TIME) + rr->ThisAPInterval = MIN_UPDATE_REFRESH_TIME; + LogInfo("SetRecordRetry refresh in %d of %d for %s", + rr->ThisAPInterval/mDNSPlatformOneSecond, (rr->expire - m->timenow)/mDNSPlatformOneSecond, ARDisplayString(m, rr)); + } + else + { + rr->ThisAPInterval = MIN_UPDATE_REFRESH_TIME; + LogInfo("SetRecordRetry clamping to min refresh in %d of %d for %s", + rr->ThisAPInterval/mDNSPlatformOneSecond, (rr->expire - m->timenow)/mDNSPlatformOneSecond, ARDisplayString(m, rr)); + } + return; + } -Revision 1.221 2005/10/05 17:27:48 herscher -<rdar://problem/4272516> Change 200ms delay to 10ms + rr->expire = 0; -Revision 1.220 2005/09/24 01:10:09 cheshire -Fix comment typos + rr->ThisAPInterval = rr->ThisAPInterval * QuestionIntervalStep; // Same Retry logic as Unicast Queries + if (rr->ThisAPInterval < INIT_RECORD_REG_INTERVAL) + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + if (rr->ThisAPInterval > MAX_RECORD_REG_INTERVAL) + rr->ThisAPInterval = MAX_RECORD_REG_INTERVAL; -Revision 1.219 2005/09/22 07:28:25 herscher -Double the delay to 200000 usec after sending out a DNS query + LogInfo("SetRecordRetry retry in %d ms for %s", rr->ThisAPInterval, ARDisplayString(m, rr)); +} -Revision 1.218 2005/09/13 01:06:14 herscher -<rdar://problem/4248878> Add 100ms delay in sendQuery. +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Name Server List Management +#endif -Revision 1.217 2005/08/04 18:08:24 cheshire -Update comments +mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr, + const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, + mDNSBool reqAAAA, mDNSBool reqDO) +{ + DNSServer **p = &m->DNSServers; + DNSServer *tmp = mDNSNULL; -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 + if ((NumUnicastDNSServers + 1) > MAX_UNICAST_DNS_SERVERS) + { + LogMsg("mDNS_AddDNSServer: DNS server limit of %d reached, not adding this server", MAX_UNICAST_DNS_SERVERS); + return mDNSNULL; + } -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 + if (!d) + d = (const domainname *)""; -Revision 1.214 2005/07/29 19:46:10 ksekar -<rdar://problem/4191860> reduce polling period on failed LLQs to 15 minutes + LogInfo("mDNS_AddDNSServer(%d): Adding %#a for %##s, InterfaceID %p, serviceID %u, scoped %d, resGroupID %d req_A is %s req_AAAA is %s cell %s req_DO is %s", + NumUnicastDNSServers, addr, d->c, interface, serviceID, scoped, resGroupID, reqA ? "True" : "False", reqAAAA ? "True" : "False", + cellIntf ? "True" : "False", reqDO ? "True" : "False"); -Revision 1.213 2005/07/29 18:04:22 ksekar -<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update + while (*p) // Check if we already have this {interface,address,port,domain} tuple registered + reqA/reqAAAA bits + { + if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->serviceID == serviceID && (*p)->teststate != DNSServer_Disabled && + mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d) && + (*p)->req_A == reqA && (*p)->req_AAAA == reqAAAA) + { + if (!((*p)->flags & DNSServer_FlagDelete)) + debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface); + tmp = *p; + *p = tmp->next; + tmp->next = mDNSNULL; + } + else + { + p=&(*p)->next; + } + } -Revision 1.212 2005/07/22 19:35:50 ksekar -<rdar://problem/4188821> SUTiger: LLQ event acknowledgments are not formated correctly + // NumUnicastDNSServers is the count of active DNS servers i.e., ones that are not marked + // with DNSServer_FlagDelete. We should increment it: + // + // 1) When we add a new DNS server + // 2) When we resurrect a old DNS server that is marked with DNSServer_FlagDelete + // + // Don't increment when we resurrect a DNS server that is not marked with DNSServer_FlagDelete. + // We have already accounted for it when it was added for the first time. This case happens when + // we add DNS servers with the same address multiple times (mis-configuration). -Revision 1.211 2005/07/21 18:51:04 ksekar -<rdar://problem/4103136> mDNSResponder times out when mapping ports after sleep + if (!tmp || (tmp->flags & DNSServer_FlagDelete)) + NumUnicastDNSServers++; -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 + if (tmp) + { + tmp->flags &= ~DNSServer_FlagDelete; + *p = tmp; // move to end of list, to ensure ordering from platform layer + } + else + { + // allocate, add to list + *p = mDNSPlatformMemAllocate(sizeof(**p)); + if (!*p) + { + LogMsg("Error: mDNS_AddDNSServer - malloc"); + } + else + { + (*p)->scoped = scoped; + (*p)->interface = interface; + (*p)->serviceID = serviceID; + (*p)->addr = *addr; + (*p)->port = port; + (*p)->flags = DNSServer_FlagNew; + (*p)->teststate = /* DNSServer_Untested */ DNSServer_Passed; + (*p)->lasttest = m->timenow - INIT_UCAST_POLL_INTERVAL; + (*p)->timeout = timeout; + (*p)->cellIntf = cellIntf; + (*p)->req_A = reqA; + (*p)->req_AAAA = reqAAAA; + (*p)->req_DO = reqDO; + // We start off assuming that the DNS server is not DNSSEC aware and + // when we receive the first response to a DNSSEC question, we set + // it to true. + (*p)->DNSSECAware = mDNSfalse; + (*p)->retransDO = 0; + AssignDomainName(&(*p)->domain, d); + (*p)->next = mDNSNULL; + } + } + (*p)->penaltyTime = 0; + // We always update the ID (not just when we allocate a new instance) because we could + // be adding a new non-scoped resolver with a new ID and we want all the non-scoped + // resolvers belong to the same group. + (*p)->resGroupID = resGroupID; + return(*p); +} + +// PenalizeDNSServer is called when the number of queries to the unicast +// DNS server exceeds MAX_UCAST_UNANSWERED_QUERIES or when we receive an +// error e.g., SERV_FAIL from DNS server. +mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags) +{ + DNSServer *new; + DNSServer *orig = q->qDNSServer; + + mDNS_CheckLock(m); + + LogInfo("PenalizeDNSServer: Penalizing DNS server %#a question for question %p %##s (%s) SuppressUnusable %d", + (q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL), q, q->qname.c, DNSTypeName(q->qtype), q->SuppressUnusable); + + // If we get error from any DNS server, remember the error. If all of the servers, + // return the error, then return the first error. + if (mDNSOpaque16IsZero(q->responseFlags)) + q->responseFlags = responseFlags; + + // After we reset the qDNSServer to NULL, we could get more SERV_FAILS that might end up + // peanlizing again. + if (!q->qDNSServer) goto end; + + // If strict ordering of unicast servers needs to be preserved, we just lookup + // the next best match server below + // + // If strict ordering is not required which is the default behavior, we penalize the server + // for DNSSERVER_PENALTY_TIME. We may also use additional logic e.g., don't penalize for PTR + // in the future. + + if (!StrictUnicastOrdering) + { + LogInfo("PenalizeDNSServer: Strict Unicast Ordering is FALSE"); + // We penalize the server so that new queries don't pick this server for DNSSERVER_PENALTY_TIME + // XXX Include other logic here to see if this server should really be penalized + // + if (q->qtype == kDNSType_PTR) + { + LogInfo("PenalizeDNSServer: Not Penalizing PTR question"); + } + else + { + LogInfo("PenalizeDNSServer: Penalizing question type %d", q->qtype); + q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME); + } + } + else + { + LogInfo("PenalizeDNSServer: Strict Unicast Ordering is TRUE"); + } -Revision 1.208 2005/06/28 00:24:28 ksekar -<rdar://problem/4157823> memory smasher in conQueryCallback +end: + new = GetServerForQuestion(m, q); -Revision 1.207 2005/05/13 20:45:10 ksekar -<rdar://problem/4074400> Rapid wide-area txt record updates don't work + if (new == orig) + { + if (new) + { + LogMsg("PenalizeDNSServer: ERROR!! GetServerForQuestion returned the same server %#a:%d", &new->addr, + mDNSVal16(new->port)); + q->ThisQInterval = 0; // Inactivate this question so that we dont bombard the network + } + else + { + // When we have no more DNS servers, we might end up calling PenalizeDNSServer multiple + // times when we receive SERVFAIL from delayed packets in the network e.g., DNS server + // is slow in responding and we have sent three queries. When we repeatedly call, it is + // okay to receive the same NULL DNS server. Next time we try to send the query, we will + // realize and re-initialize the DNS servers. + LogInfo("PenalizeDNSServer: GetServerForQuestion returned the same server NULL"); + } + } + else + { + // The new DNSServer is set in DNSServerChangeForQuestion + DNSServerChangeForQuestion(m, q, new); -Revision 1.206 2005/03/31 02:19:55 cheshire -<rdar://problem/4021486> Fix build warnings -Reviewed by: Scott Herscher + if (new) + { + LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s)", + q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c); + // We want to try the next server immediately. As the question may already have backed off, reset + // the interval. We do this only the first time when we try all the DNS servers. Once we reached the end of + // list and retrying all the servers again e.g., at least one server failed to respond in the previous try, we + // use the normal backoff which is done in uDNS_CheckCurrentQuestion when we send the packet out. + if (!q->triedAllServersOnce) + { + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + SetNextQueryTime(m, q); + } + } + else + { + // We don't have any more DNS servers for this question. If some server in the list did not return + // any response, we need to keep retrying till we get a response. uDNS_CheckCurrentQuestion handles + // this case. + // + // If all servers responded with a negative response, We need to do two things. First, generate a + // negative response so that applications get a reply. We also need to reinitialize the DNS servers + // so that when the cache expires, we can restart the query. We defer this up until we generate + // a negative cache response in uDNS_CheckCurrentQuestion. + // + // Be careful not to touch the ThisQInterval here. For a normal question, when we answer the question + // in AnswerCurrentQuestionWithResourceRecord will set ThisQInterval to MaxQuestionInterval and hence + // the next query will not happen until cache expiry. If it is a long lived question, + // AnswerCurrentQuestionWithResourceRecord will not set it to MaxQuestionInterval. In that case, + // we want the normal backoff to work. + LogInfo("PenalizeDNSServer: Server for %p, %##s (%s) changed to NULL, Interval %d", q, q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + q->unansweredQueries = 0; -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 +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - authorization management +#endif -Revision 1.203 2005/03/04 03:00:03 ksekar -<rdar://problem/4026546> Retransmissions happen too early, causing registrations to conflict with themselves +mDNSlocal DomainAuthInfo *GetAuthInfoForName_direct(mDNS *m, const domainname *const name) +{ + const domainname *n = name; + while (n->c[0]) + { + DomainAuthInfo *ptr; + for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->domain, n)) + { + debugf("GetAuthInfoForName %##s Matched %##s Key name %##s", name->c, ptr->domain.c, ptr->keyname.c); + return(ptr); + } + n = (const domainname *)(n->c + 1 + n->c[0]); + } + //LogInfo("GetAuthInfoForName none found for %##s", name->c); + return mDNSNULL; +} -Revision 1.202 2005/03/01 19:29:17 ksekar -changed LogMsgs to debugfs +// MUST be called with lock held +mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name) +{ + DomainAuthInfo **p = &m->AuthInfoList; -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. + mDNS_CheckLock(m); -Revision 1.200 2005/02/25 17:47:45 ksekar -<rdar://problem/4021868> SendServiceRegistration fails on wake from sleep + // First purge any dead keys from the list + while (*p) + { + if ((*p)->deltime && m->timenow - (*p)->deltime >= 0 && AutoTunnelUnregistered(*p)) + { + DNSQuestion *q; + DomainAuthInfo *info = *p; + LogInfo("GetAuthInfoForName_internal deleting expired key %##s %##s", info->domain.c, info->keyname.c); + *p = info->next; // Cut DomainAuthInfo from list *before* scanning our question list updating AuthInfo pointers + for (q = m->Questions; q; q=q->next) + if (q->AuthInfo == info) + { + q->AuthInfo = GetAuthInfoForName_direct(m, &q->qname); + debugf("GetAuthInfoForName_internal updated q->AuthInfo from %##s to %##s for %##s (%s)", + info->domain.c, q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype)); + } + + // Probably not essential, but just to be safe, zero out the secret key data + // so we don't leave it hanging around in memory + // (where it could potentially get exposed via some other bug) + mDNSPlatformMemZero(info, sizeof(*info)); + mDNSPlatformMemFree(info); + } + else + p = &(*p)->next; + } -Revision 1.199 2005/02/25 04:21:00 cheshire -<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing + return(GetAuthInfoForName_direct(m, name)); +} + +mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name) +{ + DomainAuthInfo *d; + mDNS_Lock(m); + d = GetAuthInfoForName_internal(m, name); + mDNS_Unlock(m); + return(d); +} + +// MUST be called with the lock held +mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, + const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel) +{ + DNSQuestion *q; + DomainAuthInfo **p = &m->AuthInfoList; + if (!info || !b64keydata) { LogMsg("mDNS_SetSecretForDomain: ERROR: info %p b64keydata %p", info, b64keydata); return(mStatus_BadParamErr); } + + LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s", domain->c, keyname->c, autoTunnel ? " AutoTunnel" : ""); + + info->AutoTunnel = autoTunnel; + AssignDomainName(&info->domain, domain); + AssignDomainName(&info->keyname, keyname); + if (hostname) + AssignDomainName(&info->hostname, hostname); + else + info->hostname.c[0] = 0; + if (port) + info->port = *port; + else + info->port = zeroIPPort; + mDNS_snprintf(info->b64keydata, sizeof(info->b64keydata), "%s", b64keydata); + + if (DNSDigest_ConstructHMACKeyfromBase64(info, b64keydata) < 0) + { + LogMsg("mDNS_SetSecretForDomain: ERROR: Could not convert shared secret from base64: domain %##s key %##s %s", domain->c, keyname->c, mDNS_LoggingEnabled ? b64keydata : ""); + return(mStatus_BadParamErr); + } -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) + // Don't clear deltime until after we've ascertained that b64keydata is valid + info->deltime = 0; + + while (*p && (*p) != info) p=&(*p)->next; + if (*p) {LogInfo("mDNS_SetSecretForDomain: Domain %##s Already in list", (*p)->domain.c); return(mStatus_AlreadyRegistered);} + + // Caution: Only zero AutoTunnelHostRecord.namestorage AFTER we've determined that this is a NEW DomainAuthInfo + // being added to the list. Otherwise we risk smashing our AutoTunnel host records that are already active and in use. + info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelHostRecord.namestorage.c[0] = 0; + info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelServiceStarted = mDNSfalse; + info->AutoTunnelInnerAddress = zerov6Addr; + info->next = mDNSNULL; + *p = info; + + // Check to see if adding this new DomainAuthInfo has changed the credentials for any of our questions + for (q = m->Questions; q; q=q->next) + { + DomainAuthInfo *newinfo = GetAuthInfoForQuestion(m, q); + if (q->AuthInfo != newinfo) + { + debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)", + q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL, + newinfo ? newinfo->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype)); + q->AuthInfo = newinfo; + } + } -Revision 1.197 2005/02/24 21:56:59 ksekar -Change LogMsgs to debugfs + return(mStatus_NoError); +} -Revision 1.196 2005/02/24 21:52:28 ksekar -<rdar://problem/3922768> Remove "deferred deregistration" logic for hostnames +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - NAT Traversal +#endif -Revision 1.195 2005/02/22 17:53:08 ksekar -Changed successful NAT Traversals from LogMsg to LogOperation +// Keep track of when to request/refresh the external address using NAT-PMP or UPnP/IGD, +// and do so when necessary +mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) +{ + mStatus err = mStatus_NoError; + + if (!m->NATTraversals) + { + m->retryGetAddr = NonZeroTime(m->timenow + 0x78000000); + LogInfo("uDNS_RequestAddress: Setting retryGetAddr to future"); + } + else if (m->timenow - m->retryGetAddr >= 0) + { + if (mDNSv4AddrIsRFC1918(&m->Router.ip.v4)) + { + static NATAddrRequest req = {NATMAP_VERS, NATOp_AddrRequest}; + static mDNSu8* start = (mDNSu8*)&req; + mDNSu8* end = start + sizeof(NATAddrRequest); + err = mDNSPlatformSendUDP(m, start, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); + debugf("uDNS_RequestAddress: Sent NAT-PMP external address request %d", err); + +#ifdef _LEGACY_NAT_TRAVERSAL_ + if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) + { + LNT_SendDiscoveryMsg(m); + debugf("uDNS_RequestAddress: LNT_SendDiscoveryMsg"); + } + else + { + mStatus lnterr = LNT_GetExternalAddress(m); + if (lnterr) + LogMsg("uDNS_RequestAddress: LNT_GetExternalAddress returned error %d", lnterr); + + err = err ? err : lnterr; // NAT-PMP error takes precedence + } +#endif // _LEGACY_NAT_TRAVERSAL_ + } -Revision 1.194 2005/02/15 18:38:03 ksekar -<rdar://problem/3967876> change expected/redundant log messages to debugfs. + // Always update the interval and retry time, so that even if we fail to send the + // packet, we won't spin in an infinite loop repeatedly failing to send the packet + if (m->retryIntervalGetAddr < NATMAP_INIT_RETRY) + { + m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + } + else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) + { + m->retryIntervalGetAddr *= 2; + } + else + { + m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; + } -Revision 1.193 2005/02/15 01:17:48 ksekar -Fixed build failure. + m->retryGetAddr = NonZeroTime(m->timenow + m->retryIntervalGetAddr); + } + else + { + debugf("uDNS_RequestAddress: Not time to send address request"); + } -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. + // Always update NextScheduledNATOp, even if we didn't change retryGetAddr, so we'll + // be called when we need to send the request(s) + if (m->NextScheduledNATOp - m->retryGetAddr > 0) + m->NextScheduledNATOp = m->retryGetAddr; -Revision 1.191 2005/02/14 18:26:51 ksekar -<rdar://problem/4005569> mDNSResponder complains about bad LLQ Opcode 2 + return err; +} -Revision 1.190 2005/02/11 19:44:06 shersche -Remove extra semicolon at end of line +mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool usePCP) +{ + mStatus err = mStatus_NoError; -Revision 1.189 2005/02/10 21:07:02 ksekar -Don't goto error in ReceiveNATAddrResponse if we receive a malformatted response + if (!info) + { + LogMsg("uDNS_SendNATMsg called unexpectedly with NULL info"); + return mStatus_BadParamErr; + } + + // send msg if the router's address is private (which means it's non-zero) + if (mDNSv4AddrIsRFC1918(&m->Router.ip.v4)) + { + if (!usePCP) + { + if (!info->sentNATPMP) + { + if (info->Protocol) + { + static NATPortMapRequest NATPortReq; + static const mDNSu8* end = (mDNSu8 *)&NATPortReq + sizeof(NATPortMapRequest); + mDNSu8 *p = (mDNSu8 *)&NATPortReq.NATReq_lease; + + NATPortReq.vers = NATMAP_VERS; + NATPortReq.opcode = info->Protocol; + NATPortReq.unused = zeroID; + NATPortReq.intport = info->IntPort; + NATPortReq.extport = info->RequestedPort; + p[0] = (mDNSu8)((info->NATLease >> 24) & 0xFF); + p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF); + p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF); + p[3] = (mDNSu8)( info->NATLease & 0xFF); + + err = mDNSPlatformSendUDP(m, (mDNSu8 *)&NATPortReq, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); + debugf("uDNS_SendNATMsg: Sent NAT-PMP mapping request %d", err); + } + + // In case the address request already went out for another NAT-T, + // set the NewAddress to the currently known global external address, so + // Address-only operations will get the callback immediately + info->NewAddress = m->ExtAddress; + + // Remember that we just sent a NAT-PMP packet, so we won't resend one later. + // We do this because the NAT-PMP "Unsupported Version" response has no + // information about the (PCP) request that triggered it, so we must send + // NAT-PMP requests for all operations. Without this, we'll send n PCP + // requests for n operations, receive n NAT-PMP "Unsupported Version" + // responses, and send n NAT-PMP requests for each of those responses, + // resulting in (n + n^2) packets sent. We only want to send 2n packets: + // n PCP requests followed by n NAT-PMP requests. + info->sentNATPMP = mDNStrue; + } + } + else + { + PCPMapRequest req; + mDNSu8* start = (mDNSu8*)&req; + mDNSu8* end = start + sizeof(req); + mDNSu8* p = (mDNSu8*)&req.lifetime; + + req.version = PCP_VERS; + req.opCode = PCPOp_Map; + req.reserved = zeroID; + + p[0] = (mDNSu8)((info->NATLease >> 24) & 0xFF); + p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF); + p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF); + p[3] = (mDNSu8)( info->NATLease & 0xFF); + + mDNSAddrMapIPv4toIPv6(&m->AdvertisedV4.ip.v4, &req.clientAddr); + + req.nonce[0] = m->PCPNonce[0]; + req.nonce[1] = m->PCPNonce[1]; + req.nonce[2] = m->PCPNonce[2]; + + req.protocol = (info->Protocol == NATOp_MapUDP ? PCPProto_UDP : PCPProto_TCP); + + req.reservedMapOp[0] = 0; + req.reservedMapOp[1] = 0; + req.reservedMapOp[2] = 0; + + if (info->Protocol) + req.intPort = info->IntPort; + else + req.intPort = DiscardPort; + req.extPort = info->RequestedPort; + + // Since we only support IPv4, even if using the all-zeros address, map it, so + // the PCP gateway will give us an IPv4 address & not an IPv6 address. + mDNSAddrMapIPv4toIPv6(&info->NewAddress, &req.extAddress); + + err = mDNSPlatformSendUDP(m, start, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); + debugf("uDNS_SendNATMsg: Sent PCP Mapping request %d", err); + + // Unset the sentNATPMP flag, so that we'll send a NAT-PMP packet if we + // receive a NAT-PMP "Unsupported Version" packet. This will result in every + // renewal, retransmission, etc. being tried first as PCP, then if a NAT-PMP + // "Unsupported Version" response is received, fall-back & send the request + // using NAT-PMP. + info->sentNATPMP = mDNSfalse; -Revision 1.188 2005/02/10 02:02:44 ksekar -Remove double semi-colon +#ifdef _LEGACY_NAT_TRAVERSAL_ + if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) + { + LNT_SendDiscoveryMsg(m); + debugf("uDNS_SendNATMsg: LNT_SendDiscoveryMsg"); + } + else + { + mStatus lnterr = LNT_MapPort(m, info); + if (lnterr) + LogMsg("uDNS_SendNATMsg: LNT_MapPort returned error %d", lnterr); + + err = err ? err : lnterr; // PCP error takes precedence + } +#endif // _LEGACY_NAT_TRAVERSAL_ + } + } -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 + return(err); +} -Revision 1.186 2005/02/04 21:56:29 ksekar -<rdar://problem/3984374> Simultaneous port map requests sometimes fail -- Refinement to previous checkin. +mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks) +{ + mDNSu32 when = NonZeroTime(m->timenow + waitTicks); + NATTraversalInfo *n; + for (n = m->NATTraversals; n; n=n->next) + { + n->ExpiryTime = 0; // Mark this mapping as expired + n->retryInterval = NATMAP_INIT_RETRY; + n->retryPortMap = when; + n->lastSuccessfulProtocol = NATTProtocolNone; + if (!n->Protocol) n->NewResult = mStatus_NoError; +#ifdef _LEGACY_NAT_TRAVERSAL_ + if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } +#endif // _LEGACY_NAT_TRAVERSAL_ + } -Revision 1.185 2005/02/03 23:48:22 ksekar -<rdar://problem/3984374> Simultaneous port map requests sometimes fail + m->PCPNonce[0] = mDNSRandom(-1); + m->PCPNonce[1] = mDNSRandom(-1); + m->PCPNonce[2] = mDNSRandom(-1); + m->retryIntervalGetAddr = 0; + m->retryGetAddr = when; -Revision 1.184 2005/02/01 19:33:29 ksekar -<rdar://problem/3985239> Keychain format too restrictive +#ifdef _LEGACY_NAT_TRAVERSAL_ + LNT_ClearState(m); +#endif // _LEGACY_NAT_TRAVERSAL_ -Revision 1.183 2005/01/27 22:57:55 cheshire -Fix compile errors on gcc4 + m->NextScheduledNATOp = m->timenow; // Need to send packets immediately +} -Revision 1.182 2005/01/25 18:55:05 ksekar -Shortened log message +mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr) +{ + static mDNSu16 last_err = 0; + NATTraversalInfo *n; -Revision 1.181 2005/01/25 02:17:32 cheshire -<rdar://problem/3971263> Don't use query ID zero in uDNS queries + if (err) + { + if (err != last_err) LogMsg("Error getting external address %d", err); + ExtAddr = zerov4Addr; + } + else + { + LogInfo("Received external IP address %.4a from NAT", &ExtAddr); + if (mDNSv4AddrIsRFC1918(&ExtAddr)) + LogMsg("Double NAT (external NAT gateway address %.4a is also a private RFC 1918 address)", &ExtAddr); + if (mDNSIPv4AddressIsZero(ExtAddr)) + err = NATErr_NetFail; // fake error to handle routers that pathologically report success with the zero address + } -Revision 1.180 2005/01/19 21:01:54 ksekar -<rdar://problem/3955355> uDNS needs to support subtype registration and browsing + // Globally remember the most recently discovered address, so it can be used in each + // new NATTraversal structure + m->ExtAddress = ExtAddr; -Revision 1.179 2005/01/19 19:15:35 ksekar -Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer + if (!err) // Success, back-off to maximum interval + m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; + else if (!last_err) // Failure after success, retry quickly (then back-off exponentially) + m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + // else back-off normally in case of pathological failures -Revision 1.178 2005/01/17 23:47:58 cheshire -<rdar://problem/3904954> Wide-area services not found on little-endian + m->retryGetAddr = m->timenow + m->retryIntervalGetAddr; + if (m->NextScheduledNATOp - m->retryGetAddr > 0) + m->NextScheduledNATOp = m->retryGetAddr; -Revision 1.177 2005/01/17 23:41:26 cheshire -Fix compile errors + last_err = err; + + for (n = m->NATTraversals; n; n=n->next) + { + // We should change n->NewAddress only when n is one of: + // 1) a mapping operation that most recently succeeded using NAT-PMP or UPnP/IGD, + // because such an operation needs the update now. If the lastSuccessfulProtocol + // is currently none, then natTraversalHandlePortMapReplyWithAddress() will be + // called should NAT-PMP or UPnP/IGD succeed in the future. + // 2) an address-only operation that did not succeed via PCP, because when such an + // operation succeeds via PCP, it's for the TCP discard port just to learn the + // address. And that address may be different than the external address + // discovered via NAT-PMP or UPnP/IGD. If the lastSuccessfulProtocol + // is currently none, we must update the NewAddress as PCP may not succeed. + if (!mDNSSameIPv4Address(n->NewAddress, ExtAddr) && + (n->Protocol ? + (n->lastSuccessfulProtocol == NATTProtocolNATPMP || n->lastSuccessfulProtocol == NATTProtocolUPNPIGD) : + (n->lastSuccessfulProtocol != NATTProtocolPCP))) + { + // Needs an update immediately + n->NewAddress = ExtAddr; + n->ExpiryTime = 0; + n->retryInterval = NATMAP_INIT_RETRY; + n->retryPortMap = m->timenow; +#ifdef _LEGACY_NAT_TRAVERSAL_ + if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } +#endif // _LEGACY_NAT_TRAVERSAL_ -Revision 1.176 2005/01/17 21:03:04 cheshire -<rdar://problem/3904954> Wide-area services not found on little-endian + m->NextScheduledNATOp = m->timenow; // Need to send packets immediately + } + } +} + +// Both places that call NATSetNextRenewalTime() update m->NextScheduledNATOp correctly afterwards +mDNSlocal void NATSetNextRenewalTime(mDNS *const m, NATTraversalInfo *n) +{ + n->retryInterval = (n->ExpiryTime - m->timenow)/2; + if (n->retryInterval < NATMAP_MIN_RETRY_INTERVAL) // Min retry interval is 2 seconds + n->retryInterval = NATMAP_MIN_RETRY_INTERVAL; + n->retryPortMap = m->timenow + n->retryInterval; +} + +mDNSlocal void natTraversalHandlePortMapReplyWithAddress(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSv4Addr extaddr, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol) +{ + const char *prot = n->Protocol == 0 ? "Add" : n->Protocol == NATOp_MapUDP ? "UDP" : n->Protocol == NATOp_MapTCP ? "TCP" : "???"; + (void)prot; + n->NewResult = err; + if (err || lease == 0 || mDNSIPPortIsZero(extport)) + { + LogInfo("natTraversalHandlePortMapReplyWithAddress: %p Response %s Port %5d External %.4a:%d lease %d error %d", + n, prot, mDNSVal16(n->IntPort), &extaddr, mDNSVal16(extport), lease, err); + n->retryInterval = NATMAP_MAX_RETRY_INTERVAL; + n->retryPortMap = m->timenow + NATMAP_MAX_RETRY_INTERVAL; + // No need to set m->NextScheduledNATOp here, since we're only ever extending the m->retryPortMap time + if (err == NATErr_Refused) n->NewResult = mStatus_NATPortMappingDisabled; + else if (err > NATErr_None && err <= NATErr_Opcode) n->NewResult = mStatus_NATPortMappingUnsupported; + } + else + { + if (lease > 999999999UL / mDNSPlatformOneSecond) + lease = 999999999UL / mDNSPlatformOneSecond; + n->ExpiryTime = NonZeroTime(m->timenow + lease * mDNSPlatformOneSecond); + + if (!mDNSSameIPv4Address(n->NewAddress, extaddr) || !mDNSSameIPPort(n->RequestedPort, extport)) + LogInfo("natTraversalHandlePortMapReplyWithAddress: %p %s Response %s Port %5d External %.4a:%d changed to %.4a:%d lease %d", + n, + (n->lastSuccessfulProtocol == NATTProtocolNone ? "None " : + n->lastSuccessfulProtocol == NATTProtocolNATPMP ? "NAT-PMP " : + n->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" : + n->lastSuccessfulProtocol == NATTProtocolPCP ? "PCP " : + /* else */ "Unknown " ), + prot, mDNSVal16(n->IntPort), &n->NewAddress, mDNSVal16(n->RequestedPort), + &extaddr, mDNSVal16(extport), lease); + + n->InterfaceID = InterfaceID; + n->NewAddress = extaddr; + if (n->Protocol) n->RequestedPort = extport; // Don't report the (PCP) external port to address-only operations + n->lastSuccessfulProtocol = protocol; + + NATSetNextRenewalTime(m, n); // Got our port mapping; now set timer to renew it at halfway point + m->NextScheduledNATOp = m->timenow; // May need to invoke client callback immediately + } +} -Revision 1.175 2005/01/15 00:56:41 ksekar -<rdar://problem/3954575> Unicast services don't disappear when logging -out of VPN +// To be called for NAT-PMP or UPnP/IGD mappings, to use currently discovered (global) address +mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol) +{ + natTraversalHandlePortMapReplyWithAddress(m, n, InterfaceID, err, m->ExtAddress, extport, lease, protocol); +} -Revision 1.174 2005/01/14 18:44:28 ksekar -<rdar://problem/3954609> mDNSResponder is crashing when changing domains +// Must be called with the mDNS_Lock held +mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal) +{ + NATTraversalInfo **n; -Revision 1.173 2005/01/14 18:34:22 ksekar -<rdar://problem/3954571> Services registered outside of firewall don't succeed after location change + LogInfo("mDNS_StartNATOperation_internal %p Protocol %d IntPort %d RequestedPort %d NATLease %d", traversal, + traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease); -Revision 1.172 2005/01/11 22:50:52 ksekar -Fixed constant naming (was using kLLQ_DefLease for update leases) + // Note: It important that new traversal requests are appended at the *end* of the list, not prepended at the start + for (n = &m->NATTraversals; *n; n=&(*n)->next) + { + if (traversal == *n) + { + LogMsg("Error! Tried to add a NAT traversal that's already in the active list: request %p Prot %d Int %d TTL %d", + traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease); + #if ForceAlerts + *(long*)0 = 0; + #endif + return(mStatus_AlreadyRegistered); + } + if (traversal->Protocol && traversal->Protocol == (*n)->Protocol && mDNSSameIPPort(traversal->IntPort, (*n)->IntPort) && + !mDNSSameIPPort(traversal->IntPort, SSHPort)) + LogMsg("Warning: Created port mapping request %p Prot %d Int %d TTL %d " + "duplicates existing port mapping request %p Prot %d Int %d TTL %d", + traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease, + *n, (*n)->Protocol, mDNSVal16((*n)->IntPort), (*n)->NATLease); + } -Revision 1.171 2005/01/10 04:52:49 ksekar -Changed LogMsg to debugf + // Initialize necessary fields + traversal->next = mDNSNULL; + traversal->ExpiryTime = 0; + traversal->retryInterval = NATMAP_INIT_RETRY; + traversal->retryPortMap = m->timenow; + traversal->NewResult = mStatus_NoError; + traversal->lastSuccessfulProtocol = NATTProtocolNone; + traversal->sentNATPMP = mDNSfalse; + traversal->ExternalAddress = onesIPv4Addr; + traversal->NewAddress = zerov4Addr; + traversal->ExternalPort = zeroIPPort; + traversal->Lifetime = 0; + traversal->Result = mStatus_NoError; + + // set default lease if necessary + if (!traversal->NATLease) traversal->NATLease = NATMAP_DEFAULT_LEASE; -Revision 1.170 2005/01/08 00:50:05 ksekar -Fixed spelling mistake in log msg +#ifdef _LEGACY_NAT_TRAVERSAL_ + mDNSPlatformMemZero(&traversal->tcpInfo, sizeof(traversal->tcpInfo)); +#endif // _LEGACY_NAT_TRAVERSAL_ -Revision 1.169 2005/01/08 00:42:18 ksekar -<rdar://problem/3922758> Clean up syslog messages + if (!m->NATTraversals) // If this is our first NAT request, kick off an address request too + { + m->retryGetAddr = m->timenow; + m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + } -Revision 1.168 2004/12/23 23:22:47 ksekar -<rdar://problem/3933606> Unicast known answers "name" pointers point to garbage stack memory + // If this is an address-only operation, initialize to the current global address, + // or (in non-PCP environments) we won't know the address until the next external + // address request/response. + if (!traversal->Protocol) + { + traversal->NewAddress = m->ExtAddress; + } -Revision 1.167 2004/12/22 22:25:47 ksekar -<rdar://problem/3734265> NATPMP: handle location changes + m->NextScheduledNATOp = m->timenow; // This will always trigger sending the packet ASAP, and generate client callback if necessary -Revision 1.166 2004/12/22 00:04:12 ksekar -<rdar://problem/3930324> mDNSResponder crashing in ReceivePortMapReply + *n = traversal; // Append new NATTraversalInfo to the end of our list -Revision 1.165 2004/12/18 03:14:22 cheshire -DblNAT -> DoubleNAT + return(mStatus_NoError); +} -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) +// Must be called with the mDNS_Lock held +mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) +{ + mDNSBool unmap = mDNStrue; + NATTraversalInfo *p; + NATTraversalInfo **ptr = &m->NATTraversals; -Revision 1.163 2004/12/17 03:51:53 ksekar -<rdar://problem/3920991> Don't update TXT record if service registration fails + while (*ptr && *ptr != traversal) ptr=&(*ptr)->next; + if (*ptr) *ptr = (*ptr)->next; // If we found it, cut this NATTraversalInfo struct from our list + else + { + LogMsg("mDNS_StopNATOperation_internal: NATTraversalInfo %p not found in list", traversal); + return(mStatus_BadReferenceErr); + } -Revision 1.162 2004/12/17 01:29:11 ksekar -<rdar://problem/3920598> Questions can go deaf on location changes + LogInfo("mDNS_StopNATOperation_internal %p %d %d %d %d", traversal, + traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease); -Revision 1.161 2004/12/16 20:42:02 cheshire -Fix compiler warnings + if (m->CurrentNATTraversal == traversal) + m->CurrentNATTraversal = m->CurrentNATTraversal->next; -Revision 1.160 2004/12/16 20:13:00 cheshire -<rdar://problem/3324626> Cache memory management improvements + // If there is a match for the operation being stopped, don't send a deletion request (unmap) + for (p = m->NATTraversals; p; p=p->next) + { + if (traversal->Protocol ? + ((traversal->Protocol == p->Protocol && mDNSSameIPPort(traversal->IntPort, p->IntPort)) || + (!p->Protocol && traversal->Protocol == NATOp_MapTCP && mDNSSameIPPort(traversal->IntPort, DiscardPort))) : + (!p->Protocol || (p->Protocol == NATOp_MapTCP && mDNSSameIPPort(p->IntPort, DiscardPort)))) + { + LogInfo("Warning: Removed port mapping request %p Prot %d Int %d TTL %d " + "duplicates existing port mapping request %p Prot %d Int %d TTL %d", + traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease, + p, p->Protocol, mDNSVal16( p->IntPort), p->NATLease); + unmap = mDNSfalse; + } + } -Revision 1.159 2004/12/15 02:11:22 ksekar -<rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness + if (traversal->ExpiryTime && unmap) + { + traversal->NATLease = 0; + traversal->retryInterval = 0; + + // In case we most recently sent NAT-PMP, we need to set sentNATPMP to false so + // that we'll send a NAT-PMP request to destroy the mapping. We do this because + // the NATTraversal struct has already been cut from the list, and the client + // layer will destroy the memory upon returning from this function, so we can't + // try PCP first and then fall-back to NAT-PMP. That is, if we most recently + // created/renewed the mapping using NAT-PMP, we need to destroy it using NAT-PMP + // now, because we won't get a chance later. + traversal->sentNATPMP = mDNSfalse; + + // Both NAT-PMP & PCP RFCs state that the suggested port in deletion requests + // should be zero. And for PCP, the suggested external address should also be + // zero, specifically, the all-zeros IPv4-mapped address, since we would only + // would have requested an IPv4 address. + traversal->RequestedPort = zeroIPPort; + traversal->NewAddress = zerov4Addr; + + uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP); + } -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 + // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up + #ifdef _LEGACY_NAT_TRAVERSAL_ + { + mStatus err = LNT_UnmapPort(m, traversal); + if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %d", err); + } + #endif // _LEGACY_NAT_TRAVERSAL_ + + return(mStatus_NoError); +} + +mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal) +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StartNATOperation_internal(m, traversal); + mDNS_Unlock(m); + return(status); +} + +mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal) +{ + mStatus status; + mDNS_Lock(m); + status = mDNS_StopNATOperation_internal(m, traversal); + mDNS_Unlock(m); + return(status); +} -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 +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Long-Lived Queries +#endif -Revision 1.156 2004/12/15 01:18:57 ksekar -<rdar://problem/3825979> Call DeregisterService on nat port map failure +// Lock must be held -- otherwise m->timenow is undefined +mDNSlocal void StartLLQPolling(mDNS *const m, DNSQuestion *q) +{ + debugf("StartLLQPolling: %##s", q->qname.c); + q->state = LLQ_Poll; + q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + // We want to send our poll query ASAP, but the "+ 1" is because if we set the time to now, + // we risk causing spurious "SendQueries didn't send all its queries" log messages + q->LastQTime = m->timenow - q->ThisQInterval + 1; + SetNextQueryTime(m, q); +#if APPLE_OSX_mDNSResponder + UpdateAutoTunnelDomainStatuses(m); +#endif +} + +mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *const question, const LLQOptData *const data) +{ + AuthRecord rr; + ResourceRecord *opt = &rr.resrec; + rdataOPT *optRD; + + //!!!KRS when we implement multiple llqs per message, we'll need to memmove anything past the question section + ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); + if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return mDNSNULL; } + + // locate OptRR if it exists, set pointer to end + // !!!KRS implement me + + // format opt rr (fields not specified are zero-valued) + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt->rrclass = NormalMaxDNSMessageData; + opt->rdlength = sizeof(rdataOPT); // One option in this OPT record + opt->rdestimate = sizeof(rdataOPT); + + optRD = &rr.resrec.rdata->u.opt[0]; + optRD->opt = kDNSOpt_LLQ; + optRD->u.llq = *data; + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0); + if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; } + + return ptr; +} + +// Normally we'd just request event packets be sent directly to m->LLQNAT.ExternalPort, except... +// with LLQs over TLS/TCP we're doing a weird thing where instead of requesting packets be sent to ExternalAddress:ExternalPort +// we're requesting that packets be sent to ExternalPort, but at the source address of our outgoing TCP connection. +// Normally, after going through the NAT gateway, the source address of our outgoing TCP connection is the same as ExternalAddress, +// so this is fine, except when the TCP connection ends up going over a VPN tunnel instead. +// To work around this, if we find that the source address for our TCP connection is not a private address, we tell the Dot Mac +// LLQ server to send events to us directly at port 5353 on that address, instead of at our mapped external NAT port. + +mDNSlocal mDNSu16 GetLLQEventPort(const mDNS *const m, const mDNSAddr *const dst) +{ + mDNSAddr src; + mDNSPlatformSourceAddrForDest(&src, dst); + //LogMsg("GetLLQEventPort: src %#a for dst %#a (%d)", &src, dst, mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : 0); + return(mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : mDNSVal16(MulticastDNSPort)); +} + +// Normally called with llq set. +// May be called with llq NULL, when retransmitting a lost Challenge Response +mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const LLQOptData *llq) +{ + mDNSu8 *responsePtr = m->omsg.data; + LLQOptData llqBuf; + + if (q->tcp) { LogMsg("sendChallengeResponse: ERROR!!: question %##s (%s) tcp non-NULL", q->qname.c, DNSTypeName(q->qtype)); return; } + + if (PrivateQuery(q)) { LogMsg("sendChallengeResponse: ERROR!!: Private Query %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + + if (q->ntries++ == kLLQ_MAX_TRIES) + { + LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s", kLLQ_MAX_TRIES, q->qname.c); + StartLLQPolling(m,q); + return; + } -Revision 1.155 2004/12/14 21:21:20 ksekar -<rdar://problem/3825979> NAT-PMP: Update response format to contain "Seconds Since Boot" + if (!llq) // Retransmission: need to make a new LLQOptData + { + llqBuf.vers = kLLQ_Vers; + llqBuf.llqOp = kLLQOp_Setup; + llqBuf.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP + llqBuf.id = q->id; + llqBuf.llqlease = q->ReqLease; + llq = &llqBuf; + } -Revision 1.154 2004/12/14 20:52:27 cheshire -Add question->qnamehash and cr->resrec.namehash to log message + q->LastQTime = m->timenow; + q->ThisQInterval = q->tcp ? 0 : (kLLQ_INIT_RESEND * q->ntries * mDNSPlatformOneSecond); // If using TCP, don't need to retransmit + SetNextQueryTime(m, q); -Revision 1.153 2004/12/14 20:45:02 cheshire -Improved error logging in "unexpected answer" message + // To simulate loss of challenge response packet, uncomment line below + //if (q->ntries == 1) return; -Revision 1.152 2004/12/14 03:02:10 ksekar -<rdar://problem/3919016> Rare race condition can cause crash + InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); + responsePtr = putLLQ(&m->omsg, responsePtr, q, llq); + if (responsePtr) + { + mStatus err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL, mDNSfalse); + if (err) { LogMsg("sendChallengeResponse: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); } + } + else StartLLQPolling(m,q); +} + +mDNSlocal void SetLLQTimer(mDNS *const m, DNSQuestion *const q, const LLQOptData *const llq) +{ + mDNSs32 lease = (mDNSs32)llq->llqlease * mDNSPlatformOneSecond; + q->ReqLease = llq->llqlease; + q->LastQTime = m->timenow; + q->expire = m->timenow + lease; + q->ThisQInterval = lease/2 + mDNSRandom(lease/10); + debugf("SetLLQTimer setting %##s (%s) to %d %d", q->qname.c, DNSTypeName(q->qtype), lease/mDNSPlatformOneSecond, q->ThisQInterval/mDNSPlatformOneSecond); + SetNextQueryTime(m, q); +} + +mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const q, const LLQOptData *const llq) +{ + if (rcode && rcode != kDNSFlag1_RC_NXDomain) + { LogMsg("ERROR: recvSetupResponse %##s (%s) - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c, DNSTypeName(q->qtype)); return; } + + if (llq->llqOp != kLLQOp_Setup) + { LogMsg("ERROR: recvSetupResponse %##s (%s) - bad op %d", q->qname.c, DNSTypeName(q->qtype), llq->llqOp); return; } + + if (llq->vers != kLLQ_Vers) + { LogMsg("ERROR: recvSetupResponse %##s (%s) - bad vers %d", q->qname.c, DNSTypeName(q->qtype), llq->vers); return; } + + if (q->state == LLQ_InitialRequest) + { + //LogInfo("Got LLQ_InitialRequest"); -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) + if (llq->err) { LogMsg("recvSetupResponse - received llq->err %d from server", llq->err); StartLLQPolling(m,q); return; } -Revision 1.150 2004/12/13 20:42:41 ksekar -Fixed LogMsg + if (q->ReqLease != llq->llqlease) + debugf("recvSetupResponse: requested lease %lu, granted lease %lu", q->ReqLease, llq->llqlease); -Revision 1.149 2004/12/13 18:10:03 ksekar -Fixed LogMsg + // cache expiration in case we go to sleep before finishing setup + q->ReqLease = llq->llqlease; + q->expire = m->timenow + ((mDNSs32)llq->llqlease * mDNSPlatformOneSecond); -Revision 1.148 2004/12/13 01:18:04 ksekar -Fixed unused variable warning for non-debug builds + // update state + q->state = LLQ_SecondaryRequest; + q->id = llq->id; + q->ntries = 0; // first attempt to send response + sendChallengeResponse(m, q, llq); + } + else if (q->state == LLQ_SecondaryRequest) + { + //LogInfo("Got LLQ_SecondaryRequest"); -Revision 1.147 2004/12/12 23:51:42 ksekar -<rdar://problem/3845683> Wide-area registrations should fallback to using DHCP hostname as target + // Fix this immediately if not sooner. Copy the id from the LLQOptData into our DNSQuestion struct. This is only + // an issue for private LLQs, because we skip parts 2 and 3 of the handshake. This is related to a bigger + // problem of the current implementation of TCP LLQ setup: we're not handling state transitions correctly + // if the server sends back SERVFULL or STATIC. + if (PrivateQuery(q)) + { + LogInfo("Private LLQ_SecondaryRequest; copying id %08X%08X", llq->id.l[0], llq->id.l[1]); + q->id = llq->id; + } -Revision 1.146 2004/12/12 23:30:40 ksekar -<rdar://problem/3916987> Extra RRs not properly unlinked when parent service registration fails + if (llq->err) { LogMsg("ERROR: recvSetupResponse %##s (%s) code %d from server", q->qname.c, DNSTypeName(q->qtype), llq->err); StartLLQPolling(m,q); return; } + if (!mDNSSameOpaque64(&q->id, &llq->id)) + { LogMsg("recvSetupResponse - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering) + q->state = LLQ_Established; + q->ntries = 0; + SetLLQTimer(m, q, llq); +#if APPLE_OSX_mDNSResponder + UpdateAutoTunnelDomainStatuses(m); +#endif + } +} -Revision 1.145 2004/12/12 22:56:29 ksekar -<rdar://problem/3668508> Need to properly handle duplicate long-lived queries +mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion) +{ + DNSQuestion pktQ, *q; + if (msg->h.numQuestions && getQuestion(msg, msg->data, end, 0, &pktQ)) + { + const rdataOPT *opt = GetLLQOptData(m, msg, end); -Revision 1.144 2004/12/11 20:55:29 ksekar -<rdar://problem/3916479> Clean up registration state machines + for (q = m->Questions; q; q = q->next) + { + if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname)) + { + debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d", + q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr, + opt ? opt->u.llq.id.l[0] : 0, opt ? opt->u.llq.id.l[1] : 0, q->id.l[0], q->id.l[1], opt ? opt->u.llq.llqOp : 0); + if (q->state == LLQ_Poll) debugf("uDNS_LLQ_Events: q->state == LLQ_Poll msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID)); + if (q->state == LLQ_Poll && mDNSSameOpaque16(msg->h.id, q->TargetQID)) + { + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + + // Don't reset the state to IntialRequest as we may write that to the dynamic store + // and PrefPane might wrongly think that we are "Starting" instead of "Polling". If + // we are in polling state because of PCP/NAT-PMP disabled or DoubleNAT, next LLQNATCallback + // would kick us back to LLQInitialRequest. So, resetting the state here may not be useful. + // + // If we have a good NAT (neither PCP/NAT-PMP disabled nor Double-NAT), then we should not be + // possibly in polling state. To be safe, we want to retry from the start in that case + // as there may not be another LLQNATCallback + // + // NOTE: We can be in polling state if we cannot resolve the SOA record i.e, servAddr is set to + // all ones. In that case, we would set it in LLQ_InitialRequest as it overrides the PCP/NAT-PMP or + // Double-NAT state. + if (!mDNSAddressIsOnes(&q->servAddr) && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && + !m->LLQNAT.Result) + { + debugf("uDNS_recvLLQResponse got poll response; moving to LLQ_InitialRequest for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->state = LLQ_InitialRequest; + } + q->servPort = zeroIPPort; // Clear servPort so that startLLQHandshake will retry the GetZoneData processing + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry LLQ setup in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + *matchQuestion = q; + return uDNS_LLQ_Entire; // uDNS_LLQ_Entire means flush stale records; assume a large effective TTL + } + // Note: In LLQ Event packets, the msg->h.id does not match our q->TargetQID, because in that case the msg->h.id nonce is selected by the server + else if (opt && q->state == LLQ_Established && opt->u.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->u.llq.id, &q->id)) + { + mDNSu8 *ackEnd; + //debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags); + ackEnd = putLLQ(&m->omsg, m->omsg.data, q, &opt->u.llq); + if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, q->LocalSocket, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse); + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + debugf("uDNS_LLQ_Events: q->state == LLQ_Established msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID)); + *matchQuestion = q; + return uDNS_LLQ_Events; + } + if (opt && mDNSSameOpaque16(msg->h.id, q->TargetQID)) + { + if (q->state == LLQ_Established && opt->u.llq.llqOp == kLLQOp_Refresh && mDNSSameOpaque64(&opt->u.llq.id, &q->id) && msg->h.numAdditionals && !msg->h.numAnswers) + { + if (opt->u.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->u.llq.err); + else + { + //LogInfo("Received refresh confirmation ntries %d for %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype)); + // If we're waiting to go to sleep, then this LLQ deletion may have been the thing + // we were waiting for, so schedule another check to see if we can sleep now. + if (opt->u.llq.llqlease == 0 && m->SleepLimit) m->NextScheduledSPRetry = m->timenow; + GrantCacheExtensions(m, q, opt->u.llq.llqlease); + SetLLQTimer(m, q, &opt->u.llq); + q->ntries = 0; + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + *matchQuestion = q; + return uDNS_LLQ_Ignore; + } + if (q->state < LLQ_Established && mDNSSameAddress(srcaddr, &q->servAddr)) + { + LLQ_State oldstate = q->state; + recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, &opt->u.llq); + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + // We have a protocol anomaly here in the LLQ definition. + // Both the challenge packet from the server and the ack+answers packet have opt->u.llq.llqOp == kLLQOp_Setup. + // However, we need to treat them differently: + // The challenge packet has no answers in it, and tells us nothing about whether our cache entries + // are still valid, so this packet should not cause us to do anything that messes with our cache. + // The ack+answers packet gives us the whole truth, so we should handle it by updating our cache + // to match the answers in the packet, and only the answers in the packet. + *matchQuestion = q; + return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Entire : uDNS_LLQ_Ignore); + } + } + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + *matchQuestion = mDNSNULL; + return uDNS_LLQ_Not; +} + +// Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.) +struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; + +// tcpCallback is called to handle events (e.g. connection opening and data reception) on TCP connections for +// Private DNS operations -- private queries, private LLQs, private record updates and private service updates +mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err) +{ + tcpInfo_t *tcpInfo = (tcpInfo_t *)context; + mDNSBool closed = mDNSfalse; + mDNS *m = tcpInfo->m; + DNSQuestion *const q = tcpInfo->question; + tcpInfo_t **backpointer = + q ? &q->tcp : + tcpInfo->rr ? &tcpInfo->rr->tcp : mDNSNULL; + if (backpointer && *backpointer != tcpInfo) + LogMsg("tcpCallback: %d backpointer %p incorrect tcpInfo %p question %p rr %p", + mDNSPlatformTCPGetFD(tcpInfo->sock), *backpointer, tcpInfo, q, tcpInfo->rr); + + if (err) goto exit; + + if (ConnectionEstablished) + { + mDNSu8 *end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen; + DomainAuthInfo *AuthInfo; -Revision 1.143 2004/12/10 01:21:27 cheshire -<rdar://problem/3914089> Get rid of "LLQ Responses over TCP not currently supported" message + // Defensive coding for <rdar://problem/5546824> Crash in mDNSResponder at GetAuthInfoForName_internal + 366 + // Don't know yet what's causing this, but at least we can be cautious and try to avoid crashing if we find our pointers in an unexpected state + if (tcpInfo->rr && tcpInfo->rr->resrec.name != &tcpInfo->rr->namestorage) + LogMsg("tcpCallback: ERROR: tcpInfo->rr->resrec.name %p != &tcpInfo->rr->namestorage %p", + tcpInfo->rr->resrec.name, &tcpInfo->rr->namestorage); + if (tcpInfo->rr && tcpInfo->rr->resrec.name != &tcpInfo->rr->namestorage) return; -Revision 1.142 2004/12/08 02:03:31 ksekar -<rdar://problem/3865124> Looping on NAT Traversal error - check for -NULL RR on error + AuthInfo = tcpInfo->rr ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name) : mDNSNULL; -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.) + // connection is established - send the message + if (q && q->LongLived && q->state == LLQ_Established) + { + // Lease renewal over TCP, resulting from opening a TCP connection in sendLLQRefresh + end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen; + } + else if (q && q->LongLived && q->state != LLQ_Poll && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && !mDNSIPPortIsZero(q->servPort)) + { + // Notes: + // If we have a NAT port mapping, ExternalPort is the external port + // If we have a routable address so we don't need a port mapping, ExternalPort is the same as our own internal port + // If we need a NAT port mapping but can't get one, then ExternalPort is zero + LLQOptData llqData; // set llq rdata + llqData.vers = kLLQ_Vers; + llqData.llqOp = kLLQOp_Setup; + llqData.err = GetLLQEventPort(m, &tcpInfo->Addr); // We're using TCP; tell server what UDP port to send notifications to + LogInfo("tcpCallback: eventPort %d", llqData.err); + llqData.id = zeroOpaque64; + llqData.llqlease = kLLQ_DefLease; + InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags); + end = putLLQ(&tcpInfo->request, tcpInfo->request.data, q, &llqData); + if (!end) { LogMsg("ERROR: tcpCallback - putLLQ"); err = mStatus_UnknownErr; goto exit; } + AuthInfo = q->AuthInfo; // Need to add TSIG to this message + q->ntries = 0; // Reset ntries so that tcp/tls connection failures don't affect sendChallengeResponse failures + } + else if (q) + { + // LLQ Polling mode or non-LLQ uDNS over TCP + InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); + end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (DNSSECQuestion(q) && q->qDNSServer && !q->qDNSServer->cellIntf) + { + if (q->ProxyQuestion) + end = DNSProxySetAttributes(q, &tcpInfo->request.h, &tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData); + else + end = putDNSSECOption(&tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData); + } -Revision 1.140 2004/12/06 21:15:22 ksekar -<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + AuthInfo = q->AuthInfo; // Need to add TSIG to this message + } -Revision 1.139 2004/12/06 19:08:03 cheshire -Add clarifying comment -- CountLabels() excludes the final root label. + err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, mDNSNULL, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo, mDNSfalse); + if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %d", err); err = mStatus_UnknownErr; goto exit; } -Revision 1.138 2004/12/06 01:45:54 ksekar -Correct wording in LogMsg + // Record time we sent this question + if (q) + { + mDNS_Lock(m); + q->LastQTime = m->timenow; + if (q->ThisQInterval < (256 * mDNSPlatformOneSecond)) // Now we have a TCP connection open, make sure we wait at least 256 seconds before retrying + q->ThisQInterval = (256 * mDNSPlatformOneSecond); + SetNextQueryTime(m, q); + mDNS_Unlock(m); + } + } + else + { + long n; + const mDNSBool Read_replylen = (tcpInfo->nread < 2); // Do we need to read the replylen field first? + if (Read_replylen) // First read the two-byte length preceeding the DNS message + { + mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replylen; + n = mDNSPlatformReadTCP(sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed); + if (n < 0) + { + LogMsg("ERROR: tcpCallback - attempt to read message length failed (%d)", n); + err = mStatus_ConnFailed; + goto exit; + } + else if (closed) + { + // It's perfectly fine for this socket to close after the first reply. The server might + // be sending gratuitous replies using UDP and doesn't have a need to leave the TCP socket open. + // We'll only log this event if we've never received a reply before. + // BIND 9 appears to close an idle connection after 30 seconds. + if (tcpInfo->numReplies == 0) + { + LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); + err = mStatus_ConnFailed; + goto exit; + } + else + { + // Note that we may not be doing the best thing if an error occurs after we've sent a second request + // over this tcp connection. That is, we only track whether we've received at least one response + // which may have been to a previous request sent over this tcp connection. + if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t + DisposeTCPConn(tcpInfo); + return; + } + } -Revision 1.137 2004/12/03 20:40:35 ksekar -<rdar://problem/3865124> Looping on NAT Traversal error + tcpInfo->nread += n; + if (tcpInfo->nread < 2) goto exit; -Revision 1.136 2004/12/03 07:20:50 ksekar -<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails + tcpInfo->replylen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]); + if (tcpInfo->replylen < sizeof(DNSMessageHeader)) + { LogMsg("ERROR: tcpCallback - length too short (%d bytes)", tcpInfo->replylen); err = mStatus_UnknownErr; goto exit; } -Revision 1.135 2004/12/03 05:18:33 ksekar -<rdar://problem/3810596> mDNSResponder needs to return more specific TSIG errors + tcpInfo->reply = mDNSPlatformMemAllocate(tcpInfo->replylen); + if (!tcpInfo->reply) { LogMsg("ERROR: tcpCallback - malloc failed"); err = mStatus_NoMemoryErr; goto exit; } + } -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 + n = mDNSPlatformReadTCP(sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replylen - (tcpInfo->nread - 2), &closed); -Revision 1.133 2004/12/02 18:37:52 ksekar -<rdar://problem/3758233> Registering with port number zero should not create a port mapping + if (n < 0) + { + // If this is our only read for this invokation, and it fails, then that's bad. + // But if we did successfully read some or all of the replylen field this time through, + // and this is now our second read from the socket, then it's expected that sometimes + // there may be no more data present, and that's perfectly okay. + // Assuming failure of the second read is a problem is what caused this bug: + // <rdar://problem/15043194> mDNSResponder fails to read DNS over TCP packet correctly + if (!Read_replylen) { LogMsg("ERROR: tcpCallback - read returned %d", n); err = mStatus_ConnFailed; } + goto exit; + } + else if (closed) + { + if (tcpInfo->numReplies == 0) + { + LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); + err = mStatus_ConnFailed; + goto exit; + } + else + { + // Note that we may not be doing the best thing if an error occurs after we've sent a second request + // over this tcp connection. That is, we only track whether we've received at least one response + // which may have been to a previous request sent over this tcp connection. + if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t + DisposeTCPConn(tcpInfo); + return; + } + } -Revision 1.132 2004/12/01 20:57:19 ksekar -<rdar://problem/3873921> Wide Area Service Discovery must be split-DNS aware + tcpInfo->nread += n; -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 + if ((tcpInfo->nread - 2) == tcpInfo->replylen) + { + mDNSBool tls; + DNSMessage *reply = tcpInfo->reply; + mDNSu8 *end = (mDNSu8 *)tcpInfo->reply + tcpInfo->replylen; + mDNSAddr Addr = tcpInfo->Addr; + mDNSIPPort Port = tcpInfo->Port; + mDNSIPPort srcPort = zeroIPPort; + tcpInfo->numReplies++; + tcpInfo->reply = mDNSNULL; // Detach reply buffer from tcpInfo_t, to make sure client callback can't cause it to be disposed + tcpInfo->nread = 0; + tcpInfo->replylen = 0; + + // If we're going to dispose this connection, do it FIRST, before calling client callback + // Note: Sleep code depends on us clearing *backpointer here -- it uses the clearing of rr->tcp + // as the signal that the DNS deregistration operation with the server has completed, and the machine may now sleep + // If we clear the tcp pointer in the question, mDNSCoreReceiveResponse cannot find a matching question. Hence + // we store the minimal information i.e., the source port of the connection in the question itself. + // Dereference sock before it is disposed in DisposeTCPConn below. + + if (sock->flags & kTCPSocketFlags_UseTLS) tls = mDNStrue; + else tls = mDNSfalse; + + if (q && q->tcp) {srcPort = q->tcp->SrcPort; q->tcpSrcPort = srcPort;} + + if (backpointer) + if (!q || !q->LongLived || m->SleepState) + { *backpointer = mDNSNULL; DisposeTCPConn(tcpInfo); } + + mDNSCoreReceive(m, reply, end, &Addr, Port, tls ? (mDNSAddr *)1 : mDNSNULL, srcPort, 0); + // USE CAUTION HERE: Invoking mDNSCoreReceive may have caused the environment to change, including canceling this operation itself + + mDNSPlatformMemFree(reply); + return; + } + } -Revision 1.130 2004/12/01 02:43:23 cheshire -Don't call StatusCallback if function pointer is null +exit: -Revision 1.129 2004/11/30 23:51:06 cheshire -Remove double semicolons + if (err) + { + // Clear client backpointer FIRST -- that way if one of the callbacks cancels its operation + // we won't end up double-disposing our tcpInfo_t + if (backpointer) *backpointer = mDNSNULL; -Revision 1.128 2004/11/25 01:48:30 ksekar -<rdar://problem/3878991> Logging into VPN does not trigger registration of address record + mDNS_Lock(m); // Need to grab the lock to get m->timenow -Revision 1.127 2004/11/25 01:41:36 ksekar -Changed unnecessary LogMsgs to debugfs + if (q) + { + if (q->ThisQInterval == 0) + { + // We get here when we fail to establish a new TCP/TLS connection that would have been used for a new LLQ request or an LLQ renewal. + // Note that ThisQInterval is also zero when sendChallengeResponse resends the LLQ request on an extant TCP/TLS connection. + q->LastQTime = m->timenow; + if (q->LongLived) + { + // We didn't get the chance to send our request packet before the TCP/TLS connection failed. + // We want to retry quickly, but want to back off exponentially in case the server is having issues. + // Since ThisQInterval was 0, we can't just multiply by QuestionIntervalStep, we must track the number + // of TCP/TLS connection failures using ntries. + mDNSu32 count = q->ntries + 1; // want to wait at least 1 second before retrying + + q->ThisQInterval = InitialQuestionInterval; + + for (; count; count--) + q->ThisQInterval *= QuestionIntervalStep; + + if (q->ThisQInterval > LLQ_POLL_INTERVAL) + q->ThisQInterval = LLQ_POLL_INTERVAL; + else + q->ntries++; + + LogMsg("tcpCallback: stream connection for LLQ %##s (%s) failed %d times, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ntries, q->ThisQInterval); + } + else + { + q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + SetNextQueryTime(m, q); + } + else if (NextQSendTime(q) - m->timenow > (q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL)) + { + // If we get an error and our next scheduled query for this question is more than the max interval from now, + // reset the next query to ensure we wait no longer the maximum interval from now before trying again. + q->LastQTime = m->timenow; + q->ThisQInterval = q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL; + SetNextQueryTime(m, q); + LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } -Revision 1.126 2004/11/23 23:54:17 ksekar -<rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures -can crash mDNSResponder + // We're about to dispose of the TCP connection, so we must reset the state to retry over TCP/TLS + // because sendChallengeResponse will send the query via UDP if we don't have a tcp pointer. + // Resetting to LLQ_InitialRequest will cause uDNS_CheckCurrentQuestion to call startLLQHandshake, which + // will attempt to establish a new tcp connection. + if (q->LongLived && q->state == LLQ_SecondaryRequest) + q->state = LLQ_InitialRequest; + + // ConnFailed may happen if the server sends a TCP reset or TLS fails, in which case we want to retry establishing the LLQ + // quickly rather than switching to polling mode. This case is handled by the above code to set q->ThisQInterval just above. + // If the error isn't ConnFailed, then the LLQ is in bad shape, so we switch to polling mode. + if (err != mStatus_ConnFailed) + { + if (q->LongLived && q->state != LLQ_Poll) StartLLQPolling(m, q); + } + } -Revision 1.125 2004/11/23 04:16:48 cheshire -Removed receiveMsg() routine. + mDNS_Unlock(m); -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. + DisposeTCPConn(tcpInfo); + } +} -Revision 1.123 2004/11/22 17:16:20 ksekar -<rdar://problem/3854298> Unicast services don't disappear when you disable all networking +mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + TCPSocketFlags flags, const mDNSAddr *const Addr, const mDNSIPPort Port, domainname *hostname, + DNSQuestion *const question, AuthRecord *const rr) +{ + mStatus err; + mDNSIPPort srcport = zeroIPPort; + tcpInfo_t *info; + mDNSBool useBackgroundTrafficClass; + + useBackgroundTrafficClass = question ? question->UseBackgroundTrafficClass : mDNSfalse; + + if ((flags & kTCPSocketFlags_UseTLS) && (!hostname || !hostname->c[0])) + { LogMsg("MakeTCPConn: TLS connection being setup with NULL hostname"); return mDNSNULL; } + + info = (tcpInfo_t *)mDNSPlatformMemAllocate(sizeof(tcpInfo_t)); + if (!info) { LogMsg("ERROR: MakeTCP - memallocate failed"); return(mDNSNULL); } + mDNSPlatformMemZero(info, sizeof(tcpInfo_t)); + + info->m = m; + info->sock = mDNSPlatformTCPSocket(m, flags, &srcport, useBackgroundTrafficClass); + info->requestLen = 0; + info->question = question; + info->rr = rr; + info->Addr = *Addr; + info->Port = Port; + info->reply = mDNSNULL; + info->replylen = 0; + info->nread = 0; + info->numReplies = 0; + info->SrcPort = srcport; -Revision 1.122 2004/11/19 18:00:34 ksekar -<rdar://problem/3682646> Security: use random ID for one-shot unicast queries + if (msg) + { + info->requestLen = (int) (end - ((mDNSu8*)msg)); + mDNSPlatformMemCopy(&info->request, msg, info->requestLen); + } -Revision 1.121 2004/11/19 04:24:08 ksekar -<rdar://problem/3682609> Security: Enforce a "window" on one-shot wide-area queries + if (!info->sock) { LogMsg("MakeTCPConn: unable to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); } + err = mDNSPlatformTCPConnect(info->sock, Addr, Port, hostname, (question ? question->InterfaceID : mDNSNULL), tcpCallback, info); + + // Probably suboptimal here. + // Instead of returning mDNSNULL here on failure, we should probably invoke the callback with an error code. + // That way clients can put all the error handling and retry/recovery code in one place, + // instead of having to handle immediate errors in one place and async errors in another. + // Also: "err == mStatus_ConnEstablished" probably never happens. + + // Don't need to log "connection failed" in customer builds -- it happens quite often during sleep, wake, configuration changes, etc. + if (err == mStatus_ConnEstablished) { tcpCallback(info->sock, info, mDNStrue, mStatus_NoError); } + else if (err != mStatus_ConnPending ) { LogInfo("MakeTCPConn: connection failed"); DisposeTCPConn(info); return(mDNSNULL); } + return(info); +} + +mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp) +{ + mDNSPlatformTCPCloseConnection(tcp->sock); + if (tcp->reply) mDNSPlatformMemFree(tcp->reply); + mDNSPlatformMemFree(tcp); +} + +// Lock must be held +mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) +{ + if (m->LLQNAT.clientContext != mDNSNULL) // LLQNAT just started, give it some time + { + LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + return; + } -Revision 1.120 2004/11/19 02:32:43 ksekar -<rdar://problem/3682608> Wide-Area Security: Add LLQ-ID to events + // Either we don't have {PCP, NAT-PMP, UPnP/IGD} support (ExternalPort is zero) or behind a Double NAT that may or + // may not have {PCP, NAT-PMP, UPnP/IGD} support (NATResult is non-zero) + if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result) + { + LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d", + q->qname.c, DNSTypeName(q->qtype), mDNSVal16(m->LLQNAT.ExternalPort), m->LLQNAT.Result); + StartLLQPolling(m, q); + return; + } -Revision 1.119 2004/11/18 23:21:24 ksekar -<rdar://problem/3764544> LLQ Security: Need to verify src port/address for LLQ handshake + if (mDNSIPPortIsZero(q->servPort)) + { + debugf("startLLQHandshake: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + q->servAddr = zeroAddr; + // We know q->servPort is zero because of check above + if (q->nta) CancelGetZoneData(m, q->nta); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); + return; + } -Revision 1.118 2004/11/18 22:58:37 ksekar -Removed old comment. + if (PrivateQuery(q)) + { + if (q->tcp) LogInfo("startLLQHandshake: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } + if (!q->nta) + { + // Normally we lookup the zone data and then call this function. And we never free the zone data + // for "PrivateQuery". But sometimes this can happen due to some race conditions. When we + // switch networks, we might end up "Polling" the network e.g., we are behind a Double NAT. + // When we poll, we free the zone information as we send the query to the server (See + // PrivateQueryGotZoneData). The NAT callback (LLQNATCallback) may happen soon after that. If we + // are still behind Double NAT, we would have returned early in this function. But we could + // have switched to a network with no NATs and we should get the zone data again. + LogInfo("startLLQHandshake: nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); + return; + } + else if (!q->nta->Host.c[0]) + { + // This should not happen. If it happens, we print a log and MakeTCPConn will fail if it can't find a hostname + LogMsg("startLLQHandshake: ERROR!!: nta non NULL for %##s (%s) but HostName %d NULL, LongLived %d", q->qname.c, DNSTypeName(q->qtype), q->nta->Host.c[0], q->LongLived); + } + q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); + if (!q->tcp) + q->ThisQInterval = mDNSPlatformOneSecond * 5; // If TCP failed (transient networking glitch) try again in five seconds + else + { + q->state = LLQ_SecondaryRequest; // Right now, for private DNS, we skip the four-way LLQ handshake + q->ReqLease = kLLQ_DefLease; + q->ThisQInterval = 0; + } + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + } + else + { + debugf("startLLQHandshake: m->AdvertisedV4 %#a%s Server %#a:%d%s %##s (%s)", + &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "", + &q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "", + q->qname.c, DNSTypeName(q->qtype)); -Revision 1.117 2004/11/18 18:04:21 ksekar -Restore checkins lost due to repository disk failure: Update comments & <rdar://problem/3880688> + if (q->ntries++ >= kLLQ_MAX_TRIES) + { + LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c); + StartLLQPolling(m, q); + } + else + { + mDNSu8 *end; + LLQOptData llqData; + + // set llq rdata + llqData.vers = kLLQ_Vers; + llqData.llqOp = kLLQOp_Setup; + llqData.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP + llqData.id = zeroOpaque64; + llqData.llqlease = kLLQ_DefLease; + + InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); + end = putLLQ(&m->omsg, m->omsg.data, q, &llqData); + if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); StartLLQPolling(m,q); return; } + + mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL, mDNSfalse); + + // update question state + q->state = LLQ_InitialRequest; + q->ReqLease = kLLQ_DefLease; + q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + } + } +} -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>. +// forward declaration so GetServiceTarget can do reverse lookup if needed +mDNSlocal void GetStaticHostname(mDNS *m); -Revision 1.xxx 2004/11/17 00:45:28 ksekar -<rdar://problem/3880688> Result of putUpdateLease not error-checked +mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr) +{ + debugf("GetServiceTarget %##s", rr->resrec.name->c); -Revision 1.116 2004/11/16 01:41:47 ksekar -Fixed typo in debugf + if (!rr->AutoTarget) // If not automatically tracking this host's current name, just return the existing target + return(&rr->resrec.rdata->u.srv.target); + else + { +#if APPLE_OSX_mDNSResponder + DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + if (AuthInfo && AuthInfo->AutoTunnel) + { + StartServerTunnel(m, AuthInfo); + if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0) return(mDNSNULL); + debugf("GetServiceTarget: Returning %##s", AuthInfo->AutoTunnelHostRecord.namestorage.c); + return(&AuthInfo->AutoTunnelHostRecord.namestorage); + } + else +#endif // APPLE_OSX_mDNSResponder + { + const int srvcount = CountLabels(rr->resrec.name); + HostnameInfo *besthi = mDNSNULL, *hi; + int best = 0; + for (hi = m->Hostnames; hi; hi = hi->next) + if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh || + hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) + { + int x, hostcount = CountLabels(&hi->fqdn); + for (x = hostcount < srvcount ? hostcount : srvcount; x > 0 && x > best; x--) + if (SameDomainName(SkipLeadingLabels(rr->resrec.name, srvcount - x), SkipLeadingLabels(&hi->fqdn, hostcount - x))) + { best = x; besthi = hi; } + } + + if (besthi) return(&besthi->fqdn); + } + if (m->StaticHostname.c[0]) return(&m->StaticHostname); + else GetStaticHostname(m); // asynchronously do reverse lookup for primary IPv4 address + LogInfo("GetServiceTarget: Returning NULL for %s", ARDisplayString(m, rr)); + return(mDNSNULL); + } +} -Revision 1.115 2004/11/15 20:09:24 ksekar -<rdar://problem/3719050> Wide Area support for Add/Remove record +mDNSlocal const domainname *PUBLIC_UPDATE_SERVICE_TYPE = (const domainname*)"\x0B_dns-update" "\x04_udp"; +mDNSlocal const domainname *PUBLIC_LLQ_SERVICE_TYPE = (const domainname*)"\x08_dns-llq" "\x04_udp"; -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 +mDNSlocal const domainname *PRIVATE_UPDATE_SERVICE_TYPE = (const domainname*)"\x0F_dns-update-tls" "\x04_tcp"; +mDNSlocal const domainname *PRIVATE_QUERY_SERVICE_TYPE = (const domainname*)"\x0E_dns-query-tls" "\x04_tcp"; +mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE = (const domainname*)"\x0C_dns-llq-tls" "\x04_tcp"; -Revision 1.113 2004/11/13 02:29:52 ksekar -<rdar://problem/3878386> LLQ refreshes not reliable +#define ZoneDataSRV(X) ( \ + (X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \ + (X)->ZoneService == ZoneServiceQuery ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE : (const domainname*)"" ) : \ + (X)->ZoneService == ZoneServiceLLQ ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE : PUBLIC_LLQ_SERVICE_TYPE ) : (const domainname*)"") -Revision 1.112 2004/11/11 20:45:14 ksekar -<rdar://problem/3876052> self-conflict test not compatible with some BIND servers +// Forward reference: GetZoneData_StartQuery references GetZoneData_QuestionCallback, and +// GetZoneData_QuestionCallback calls GetZoneData_StartQuery +mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype); -Revision 1.111 2004/11/11 20:14:55 ksekar -<rdar://problem/3719574> Wide-Area registrations not deregistered on sleep +// GetZoneData_QuestionCallback is called from normal client callback context (core API calls allowed) +mDNSlocal void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + ZoneData *zd = (ZoneData*)question->QuestionContext; -Revision 1.110 2004/11/10 23:53:53 ksekar -Remove no longer relevant comment + debugf("GetZoneData_QuestionCallback: %s %s", AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer)); -Revision 1.109 2004/11/10 20:40:53 ksekar -<rdar://problem/3868216> LLQ mobility fragile on non-primary interface + if (!AddRecord) return; // Don't care about REMOVE events + if (AddRecord == QC_addnocache && answer->rdlength == 0) return; // Don't care about transient failure indications + if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs -Revision 1.108 2004/11/01 20:36:16 ksekar -<rdar://problem/3802395> mDNSResponder should not receive Keychain Notifications + if (answer->rrtype == kDNSType_SOA) + { + debugf("GetZoneData GOT SOA %s", RRDisplayString(m, answer)); + mDNS_StopQuery(m, question); + if (question->ThisQInterval != -1) + LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); + if (answer->rdlength) + { + AssignDomainName(&zd->ZoneName, answer->name); + zd->ZoneClass = answer->rrclass; + AssignDomainName(&zd->question.qname, &zd->ZoneName); + GetZoneData_StartQuery(m, zd, kDNSType_SRV); + } + else if (zd->CurrentSOA->c[0]) + { + DomainAuthInfo *AuthInfo = GetAuthInfoForName(m, zd->CurrentSOA); + if (AuthInfo && AuthInfo->AutoTunnel) + { + // To keep the load on the server down, we don't chop down on + // SOA lookups for AutoTunnels + LogInfo("GetZoneData_QuestionCallback: not chopping labels for %##s", zd->CurrentSOA->c); + zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd); + } + else + { + zd->CurrentSOA = (domainname *)(zd->CurrentSOA->c + zd->CurrentSOA->c[0]+1); + AssignDomainName(&zd->question.qname, zd->CurrentSOA); + GetZoneData_StartQuery(m, zd, kDNSType_SOA); + } + } + else + { + LogInfo("GetZoneData recursed to root label of %##s without finding SOA", zd->ChildName.c); + zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd); + } + } + else if (answer->rrtype == kDNSType_SRV) + { + debugf("GetZoneData GOT SRV %s", RRDisplayString(m, answer)); + mDNS_StopQuery(m, question); + if (question->ThisQInterval != -1) + LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); +// Right now we don't want to fail back to non-encrypted operations +// If the AuthInfo has the AutoTunnel field set, then we want private or nothing +// <rdar://problem/5687667> BTMM: Don't fallback to unencrypted operations when SRV lookup fails +#if 0 + if (!answer->rdlength && zd->ZonePrivate && zd->ZoneService != ZoneServiceQuery) + { + zd->ZonePrivate = mDNSfalse; // Causes ZoneDataSRV() to yield a different SRV name when building the query + GetZoneData_StartQuery(m, zd, kDNSType_SRV); // Try again, non-private this time + } + else +#endif + { + if (answer->rdlength) + { + AssignDomainName(&zd->Host, &answer->rdata->u.srv.target); + zd->Port = answer->rdata->u.srv.port; + AssignDomainName(&zd->question.qname, &zd->Host); + GetZoneData_StartQuery(m, zd, kDNSType_A); + } + else + { + zd->ZonePrivate = mDNSfalse; + zd->Host.c[0] = 0; + zd->Port = zeroIPPort; + zd->Addr = zeroAddr; + zd->ZoneDataCallback(m, mStatus_NoError, zd); + } + } + } + else if (answer->rrtype == kDNSType_A) + { + debugf("GetZoneData GOT A %s", RRDisplayString(m, answer)); + mDNS_StopQuery(m, question); + if (question->ThisQInterval != -1) + LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval); + zd->Addr.type = mDNSAddrType_IPv4; + if (answer->rdlength == 4) + zd->Addr.ip.v4 = answer->rdata->u.ipv4; + else + zd->Addr.ip.v4 = zerov4Addr; + // In order to simulate firewalls blocking our outgoing TCP connections, returning immediate ICMP errors or TCP resets, + // the code below will make us try to connect to loopback, resulting in an immediate "port unreachable" failure. + // This helps us test to make sure we handle this case gracefully + // <rdar://problem/5607082> BTMM: mDNSResponder taking 100 percent CPU after upgrading to 10.5.1 +#if 0 + zd->Addr.ip.v4.b[0] = 127; + zd->Addr.ip.v4.b[1] = 0; + zd->Addr.ip.v4.b[2] = 0; + zd->Addr.ip.v4.b[3] = 1; +#endif + // The caller needs to free the memory when done with zone data + zd->ZoneDataCallback(m, mStatus_NoError, zd); + } +} -Revision 1.107 2004/10/26 06:11:41 cheshire -Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed +// GetZoneData_StartQuery is called from normal client context (lock not held, or client callback) +mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype) +{ + if (qtype == kDNSType_SRV) + { + AssignDomainName(&zd->question.qname, ZoneDataSRV(zd)); + AppendDomainName(&zd->question.qname, &zd->ZoneName); + debugf("lookupDNSPort %##s", zd->question.qname.c); + } -Revision 1.106 2004/10/26 03:52:03 cheshire -Update checkin comments + // CancelGetZoneData can get called at any time. We should stop the question if it has not been + // stopped already. A value of -1 for ThisQInterval indicates that the question is not active + // yet. + zd->question.ThisQInterval = -1; + zd->question.InterfaceID = mDNSInterface_Any; + zd->question.flags = 0; + zd->question.Target = zeroAddr; + //zd->question.qname.c[0] = 0; // Already set + zd->question.qtype = qtype; + zd->question.qclass = kDNSClass_IN; + zd->question.LongLived = mDNSfalse; + zd->question.ExpectUnique = mDNStrue; + zd->question.ForceMCast = mDNSfalse; + zd->question.ReturnIntermed = mDNStrue; + zd->question.SuppressUnusable = mDNSfalse; + zd->question.DenyOnCellInterface = mDNSfalse; + zd->question.DenyOnExpInterface = mDNSfalse; + zd->question.SearchListIndex = 0; + zd->question.AppendSearchDomains = 0; + zd->question.RetryWithSearchDomains = mDNSfalse; + zd->question.TimeoutQuestion = 0; + zd->question.WakeOnResolve = 0; + zd->question.UseBackgroundTrafficClass = mDNSfalse; + zd->question.ValidationRequired = 0; + zd->question.ValidatingResponse = 0; + zd->question.ProxyQuestion = 0; + zd->question.qnameOrig = mDNSNULL; + zd->question.AnonInfo = mDNSNULL; + zd->question.pid = mDNSPlatformGetPID(); + zd->question.QuestionCallback = GetZoneData_QuestionCallback; + zd->question.QuestionContext = zd; + + //LogMsg("GetZoneData_StartQuery %##s (%s) %p", zd->question.qname.c, DNSTypeName(zd->question.qtype), zd->question.Private); + return(mDNS_StartQuery(m, &zd->question)); +} + +// StartGetZoneData is an internal routine (i.e. must be called with the lock already held) +mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext) +{ + DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, name); + int initialskip = (AuthInfo && AuthInfo->AutoTunnel) ? DomainNameLength(name) - DomainNameLength(&AuthInfo->domain) : 0; + ZoneData *zd = (ZoneData*)mDNSPlatformMemAllocate(sizeof(ZoneData)); + if (!zd) { LogMsg("ERROR: StartGetZoneData - mDNSPlatformMemAllocate failed"); return mDNSNULL; } + mDNSPlatformMemZero(zd, sizeof(ZoneData)); + AssignDomainName(&zd->ChildName, name); + zd->ZoneService = target; + zd->CurrentSOA = (domainname *)(&zd->ChildName.c[initialskip]); + zd->ZoneName.c[0] = 0; + zd->ZoneClass = 0; + zd->Host.c[0] = 0; + zd->Port = zeroIPPort; + zd->Addr = zeroAddr; + zd->ZonePrivate = AuthInfo && AuthInfo->AutoTunnel ? mDNStrue : mDNSfalse; + zd->ZoneDataCallback = callback; + zd->ZoneDataContext = ZoneDataContext; + + zd->question.QuestionContext = zd; + + mDNS_DropLockBeforeCallback(); // GetZoneData_StartQuery expects to be called from a normal callback, so we emulate that here + if (AuthInfo && AuthInfo->AutoTunnel && !mDNSIPPortIsZero(AuthInfo->port)) + { + LogInfo("StartGetZoneData: Bypassing SOA, SRV query for %##s", AuthInfo->domain.c); + // We bypass SOA and SRV queries if we know the hostname and port already from the configuration. + // Today this is only true for AutoTunnel. As we bypass, we need to infer a few things: + // + // 1. Zone name is the same as the AuthInfo domain + // 2. ZoneClass is kDNSClass_IN which should be a safe assumption + // + // If we want to make this bypass mechanism work for non-AutoTunnels also, (1) has to hold + // good. Otherwise, it has to be configured also. + + AssignDomainName(&zd->ZoneName, &AuthInfo->domain); + zd->ZoneClass = kDNSClass_IN; + AssignDomainName(&zd->Host, &AuthInfo->hostname); + zd->Port = AuthInfo->port; + AssignDomainName(&zd->question.qname, &zd->Host); + GetZoneData_StartQuery(m, zd, kDNSType_A); + } + else + { + if (AuthInfo && AuthInfo->AutoTunnel) LogInfo("StartGetZoneData: Not Bypassing SOA, SRV query for %##s", AuthInfo->domain.c); + AssignDomainName(&zd->question.qname, zd->CurrentSOA); + GetZoneData_StartQuery(m, zd, kDNSType_SOA); + } + mDNS_ReclaimLockAfterCallback(); + + return zd; +} + +// Returns if the question is a GetZoneData question. These questions are special in +// that they are created internally while resolving a private query or LLQs. +mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q) +{ + if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNStrue); + else return(mDNSfalse); +} + +// GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately, +// because that would result in an infinite loop (i.e. to do a private query we first need to get +// the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so +// we'd need to already know the _dns-query-tls SRV record. +// Also, as a general rule, we never do SOA queries privately +mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) // Must be called with lock held +{ + if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNSNULL); + if (q->qtype == kDNSType_SOA ) return(mDNSNULL); + return(GetAuthInfoForName_internal(m, &q->qname)); +} -Revision 1.105 2004/10/26 01:15:06 cheshire -Use "#if 0" instead of commenting out code +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - host name and interface management +#endif -Revision 1.104 2004/10/25 21:41:38 ksekar -<rdar://problem/3852958> wide-area name conflicts can cause crash +mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr); +mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr); +mDNSlocal mDNSBool IsRecordMergeable(mDNS *const m, AuthRecord *rr, mDNSs32 time); -Revision 1.103 2004/10/25 19:30:52 ksekar -<rdar://problem/3827956> Simplify dynamic host name structures +// When this function is called, service record is already deregistered. We just +// have to deregister the PTR and TXT records. +mDNSlocal void UpdateAllServiceRecords(mDNS *const m, AuthRecord *rr, mDNSBool reg) +{ + AuthRecord *r, *srvRR; -Revision 1.102 2004/10/23 01:16:00 cheshire -<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts + if (rr->resrec.rrtype != kDNSType_SRV) { LogMsg("UpdateAllServiceRecords:ERROR!! ResourceRecord not a service record %s", ARDisplayString(m, rr)); return; } -Revision 1.101 2004/10/22 20:52:07 ksekar -<rdar://problem/3799260> Create NAT port mappings for Long Lived Queries + if (reg && rr->state == regState_NoTarget) { LogMsg("UpdateAllServiceRecords:ERROR!! SRV record %s in noTarget state during registration", ARDisplayString(m, rr)); return; } -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 + LogInfo("UpdateAllServiceRecords: ResourceRecord %s", ARDisplayString(m, rr)); -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 + for (r = m->ResourceRecords; r; r=r->next) + { + if (!AuthRecord_uDNS(r)) continue; + srvRR = mDNSNULL; + if (r->resrec.rrtype == kDNSType_PTR) + srvRR = r->Additional1; + else if (r->resrec.rrtype == kDNSType_TXT) + srvRR = r->DependentOn; + if (srvRR && srvRR->resrec.rrtype != kDNSType_SRV) + LogMsg("UpdateAllServiceRecords: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR)); + if (srvRR == rr) + { + if (!reg) + { + LogInfo("UpdateAllServiceRecords: deregistering %s", ARDisplayString(m, r)); + r->SRVChanged = mDNStrue; + r->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + r->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + r->state = regState_DeregPending; + } + else + { + // Clearing SRVchanged is a safety measure. If our pevious dereg never + // came back and we had a target change, we are starting fresh + r->SRVChanged = mDNSfalse; + // if it is already registered or in the process of registering, then don't + // bother re-registering. This happens today for non-BTMM domains where the + // TXT and PTR get registered before SRV records because of the delay in + // getting the port mapping. There is no point in re-registering the TXT + // and PTR records. + if ((r->state == regState_Registered) || + (r->state == regState_Pending && r->nta && !mDNSIPv4AddressIsZero(r->nta->Addr.ip.v4))) + LogInfo("UpdateAllServiceRecords: not registering %s, state %d", ARDisplayString(m, r), r->state); + else + { + LogInfo("UpdateAllServiceRecords: registering %s, state %d", ARDisplayString(m, r), r->state); + ActivateUnicastRegistration(m, r); + } + } + } + } +} -Revision 1.98 2004/10/16 00:16:59 cheshire -<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check +// Called in normal client context (lock not held) +// Currently only supports SRV records for nat mapping +mDNSlocal void CompleteRecordNatMap(mDNS *m, NATTraversalInfo *n) +{ + const domainname *target; + domainname *srvt; + AuthRecord *rr = (AuthRecord *)n->clientContext; + debugf("SRVNatMap complete %.4a IntPort %u ExternalPort %u NATLease %u", &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), n->NATLease); -Revision 1.97 2004/10/15 23:00:18 ksekar -<rdar://problem/3799242> Need to update LLQs on location changes + if (!rr) { LogMsg("CompleteRecordNatMap called with unknown AuthRecord object"); return; } + if (!n->NATLease) { LogMsg("CompleteRecordNatMap No NATLease for %s", ARDisplayString(m, rr)); return; } -Revision 1.96 2004/10/12 23:30:44 ksekar -<rdar://problem/3609944> mDNSResponder needs to follow CNAME referrals + if (rr->resrec.rrtype != kDNSType_SRV) {LogMsg("CompleteRecordNatMap: Not a service record %s", ARDisplayString(m, rr)); return; } -Revision 1.95 2004/10/12 03:15:09 ksekar -<rdar://problem/3835612> mDNS_StartQuery shouldn't return transient no-server error + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) { LogInfo("CompleteRecordNatMap called for %s, Service deregistering", ARDisplayString(m, rr)); return; } -Revision 1.94 2004/10/12 02:49:20 ksekar -<rdar://problem/3831228> Clean up LLQ sleep/wake, error handling + if (rr->state == regState_DeregPending) { LogInfo("CompleteRecordNatMap called for %s, record in DeregPending", ARDisplayString(m, rr)); return; } -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 + // As we free the zone info after registering/deregistering with the server (See hndlRecordUpdateReply), + // we need to restart the get zone data and nat mapping request to get the latest mapping result as we can't handle it + // at this moment. Restart from the beginning. + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) + { + LogInfo("CompleteRecordNatMap called for %s but no zone information!", ARDisplayString(m, rr)); + // We need to clear out the NATinfo state so that it will result in re-acquiring the mapping + // and hence this callback called again. + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + rr->state = regState_Pending; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + return; + } -Revision 1.92 2004/10/08 03:54:35 ksekar -<rdar://problem/3831802> Refine unicast polling intervals + mDNS_Lock(m); + // Reevaluate the target always as Target could have changed while + // we were getting the port mapping (See UpdateOneSRVRecord) + target = GetServiceTarget(m, rr); + srvt = GetRRDomainNameTarget(&rr->resrec); + if (!target || target->c[0] == 0 || mDNSIPPortIsZero(n->ExternalPort)) + { + if (target && target->c[0]) + LogInfo("CompleteRecordNatMap - Target %##s for ResourceRecord %##s, ExternalPort %d", target->c, rr->resrec.name->c, mDNSVal16(n->ExternalPort)); + else + LogInfo("CompleteRecordNatMap - no target for %##s, ExternalPort %d", rr->resrec.name->c, mDNSVal16(n->ExternalPort)); + if (srvt) srvt->c[0] = 0; + rr->state = regState_NoTarget; + rr->resrec.rdlength = rr->resrec.rdestimate = 0; + mDNS_Unlock(m); + UpdateAllServiceRecords(m, rr, mDNSfalse); + return; + } + LogInfo("CompleteRecordNatMap - Target %##s for ResourceRecord %##s, ExternalPort %d", target->c, rr->resrec.name->c, mDNSVal16(n->ExternalPort)); + // This function might get called multiple times during a network transition event. Previosuly, we could + // have put the SRV record in NoTarget state above and deregistered all the other records. When this + // function gets called again with a non-zero ExternalPort, we need to set the target and register the + // other records again. + if (srvt && !SameDomainName(srvt, target)) + { + AssignDomainName(srvt, target); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Update rdlength, rdestimate, rdatahash + } -Revision 1.91 2004/09/30 17:45:34 ksekar -<rdar://problem/3821119> lots of log messages: mDNS_SetPrimaryIP: IP address unchanged + // SRVChanged is set when when the target of the SRV record changes (See UpdateOneSRVRecord). + // As a result of the target change, we might register just that SRV Record if it was + // previously registered and we have a new target OR deregister SRV (and the associated + // PTR/TXT records) if we don't have a target anymore. When we get a response from the server, + // SRVChanged state tells that we registered/deregistered because of a target change + // and hence handle accordingly e.g., if we deregistered, put the records in NoTarget state OR + // if we registered then put it in Registered state. + // + // Here, we are registering all the records again from the beginning. Treat this as first time + // registration rather than a temporary target change. + rr->SRVChanged = mDNSfalse; + + // We want IsRecordMergeable to check whether it is a record whose update can be + // sent with others. We set the time before we call IsRecordMergeable, so that + // it does not fail this record based on time. We are interested in other checks + // at this time + rr->state = regState_Pending; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + if (IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) + // Delay the record registration by MERGE_DELAY_TIME so that we can merge them + // into one update + rr->LastAPTime += MERGE_DELAY_TIME; + mDNS_Unlock(m); + // We call this always even though it may not be necessary always e.g., normal registration + // process where TXT and PTR gets registered followed by the SRV record after it gets + // the port mapping. In that case, UpdateAllServiceRecords handles the optimization. The + // update of TXT and PTR record is required if we entered noTargetState before as explained + // above. + UpdateAllServiceRecords(m, rr, mDNStrue); +} + +mDNSlocal void StartRecordNatMap(mDNS *m, AuthRecord *rr) +{ + const mDNSu8 *p; + mDNSu8 protocol; + + if (rr->resrec.rrtype != kDNSType_SRV) + { + LogInfo("StartRecordNatMap: Resource Record %##s type %d, not supported", rr->resrec.name->c, rr->resrec.rrtype); + return; + } + p = rr->resrec.name->c; + //Assume <Service Instance>.<App Protocol>.<Transport protocol>.<Name> + // Skip the first two labels to get to the transport protocol + if (p[0]) p += 1 + p[0]; + if (p[0]) p += 1 + p[0]; + if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = NATOp_MapTCP; + else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = NATOp_MapUDP; + else { LogMsg("StartRecordNatMap: could not determine transport protocol of service %##s", rr->resrec.name->c); return; } + + //LogMsg("StartRecordNatMap: clientContext %p IntPort %d srv.port %d %s", + // rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr)); + if (rr->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.Protocol = protocol; + + // Shouldn't be trying to set IntPort here -- + // BuildUpdateMessage overwrites srs->RR_SRV.resrec.rdata->u.srv.port with external (mapped) port number + rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; + rr->NATinfo.RequestedPort = rr->resrec.rdata->u.srv.port; + rr->NATinfo.NATLease = 0; // Request default lease + rr->NATinfo.clientCallback = CompleteRecordNatMap; + rr->NATinfo.clientContext = rr; + mDNS_StartNATOperation_internal(m, &rr->NATinfo); +} + +// Unlink an Auth Record from the m->ResourceRecords list. +// When a resource record enters regState_NoTarget initially, mDNS_Register_internal +// does not initialize completely e.g., it cannot check for duplicates etc. The resource +// record is temporarily left in the ResourceRecords list so that we can initialize later +// when the target is resolvable. Similarly, when host name changes, we enter regState_NoTarget +// and we do the same. + +// This UnlinkResourceRecord routine is very worrying. It bypasses all the normal cleanup performed +// by mDNS_Deregister_internal and just unceremoniously cuts the record from the active list. +// This is why re-regsitering this record was producing syslog messages like this: +// "Error! Tried to add a NAT traversal that's already in the active list" +// Right now UnlinkResourceRecord is fortunately only called by RegisterAllServiceRecords, +// which then immediately calls mDNS_Register_internal to re-register the record, which probably +// masked more serious problems. Any other use of UnlinkResourceRecord is likely to lead to crashes. +// For now we'll workaround that specific problem by explicitly calling mDNS_StopNATOperation_internal, +// but long-term we should either stop cancelling the record registration and then re-registering it, +// or if we really do need to do this for some reason it should be done via the usual +// mDNS_Deregister_internal path instead of just cutting the record from the list. + +mDNSlocal mStatus UnlinkResourceRecord(mDNS *const m, AuthRecord *const rr) +{ + AuthRecord **list = &m->ResourceRecords; + while (*list && *list != rr) list = &(*list)->next; + if (*list) + { + *list = rr->next; + rr->next = mDNSNULL; -Revision 1.90 2004/09/25 00:22:13 ksekar -<rdar://problem/3815534> Crash in uDNS_RegisterService + // Temporary workaround to cancel any active NAT mapping operation + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + if (rr->resrec.rrtype == kDNSType_SRV) rr->resrec.rdata->u.srv.port = rr->NATinfo.IntPort; + } -Revision 1.89 2004/09/24 19:14:53 cheshire -Remove unused "extern mDNS mDNSStorage" + return(mStatus_NoError); + } + LogMsg("UnlinkResourceRecord:ERROR!! - no such active record %##s", rr->resrec.name->c); + return(mStatus_NoSuchRecord); +} + +// We need to go through mDNS_Register again as we did not complete the +// full initialization last time e.g., duplicate checks. +// After we register, we will be in regState_GetZoneData. +mDNSlocal void RegisterAllServiceRecords(mDNS *const m, AuthRecord *rr) +{ + LogInfo("RegisterAllServiceRecords: Service Record %##s", rr->resrec.name->c); + // First Register the service record, we do this differently from other records because + // when it entered NoTarget state, it did not go through complete initialization + rr->SRVChanged = mDNSfalse; + UnlinkResourceRecord(m, rr); + mDNS_Register_internal(m, rr); + // Register the other records + UpdateAllServiceRecords(m, rr, mDNStrue); +} + +// Called with lock held +mDNSlocal void UpdateOneSRVRecord(mDNS *m, AuthRecord *rr) +{ + // Target change if: + // We have a target and were previously waiting for one, or + // We had a target and no longer do, or + // The target has changed + + domainname *curtarget = &rr->resrec.rdata->u.srv.target; + const domainname *const nt = GetServiceTarget(m, rr); + const domainname *const newtarget = nt ? nt : (domainname*)""; + mDNSBool TargetChanged = (newtarget->c[0] && rr->state == regState_NoTarget) || !SameDomainName(curtarget, newtarget); + mDNSBool HaveZoneData = rr->nta && !mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4); + + // Nat state change if: + // We were behind a NAT, and now we are behind a new NAT, or + // We're not behind a NAT but our port was previously mapped to a different external port + // We were not behind a NAT and now we are + + mDNSIPPort port = rr->resrec.rdata->u.srv.port; + mDNSBool NowNeedNATMAP = (rr->AutoTarget == Target_AutoHostAndNATMAP && !mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && rr->nta && !mDNSAddrIsRFC1918(&rr->nta->Addr)); + mDNSBool WereBehindNAT = (rr->NATinfo.clientContext != mDNSNULL); + mDNSBool PortWasMapped = (rr->NATinfo.clientContext && !mDNSSameIPPort(rr->NATinfo.RequestedPort, port)); // I think this is always false -- SC Sept 07 + mDNSBool NATChanged = (!WereBehindNAT && NowNeedNATMAP) || (!NowNeedNATMAP && PortWasMapped); + + (void)HaveZoneData; //unused + + LogInfo("UpdateOneSRVRecord: Resource Record %s TargetChanged %d, NewTarget %##s", ARDisplayString(m, rr), TargetChanged, nt->c); + + debugf("UpdateOneSRVRecord: %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowNeedNATMAP %d WereBehindNAT %d PortWasMapped %d NATChanged %d", + rr->resrec.name->c, newtarget, + TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged); + + mDNS_CheckLock(m); + + if (!TargetChanged && !NATChanged) return; + + // If we are deregistering the record, then ignore any NAT/Target change. + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + LogInfo("UpdateOneSRVRecord: Deregistering record, Ignoring TargetChanged %d, NATChanged %d for %##s, state %d", TargetChanged, NATChanged, + rr->resrec.name->c, rr->state); + return; + } -Revision 1.88 2004/09/23 20:48:15 ksekar -Clarify retransmission debugf messages. + if (newtarget) + LogInfo("UpdateOneSRVRecord: TargetChanged %d, NATChanged %d for %##s, state %d, newtarget %##s", TargetChanged, NATChanged, rr->resrec.name->c, rr->state, newtarget->c); + else + LogInfo("UpdateOneSRVRecord: TargetChanged %d, NATChanged %d for %##s, state %d, null newtarget", TargetChanged, NATChanged, rr->resrec.name->c, rr->state); + switch(rr->state) + { + case regState_NATMap: + // In these states, the SRV has either not yet been registered (it will get up-to-date information when it is) + // or is in the process of, or has already been, deregistered. This assumes that whenever we transition out + // of this state, we need to look at the target again. + return; + + case regState_UpdatePending: + // We are getting a Target change/NAT change while the SRV record is being updated ? + // let us not do anything for now. + return; + + case regState_NATError: + if (!NATChanged) return; + // if nat changed, register if we have a target (below) + + case regState_NoTarget: + if (!newtarget->c[0]) + { + LogInfo("UpdateOneSRVRecord: No target yet for Resource Record %s", ARDisplayString(m, rr)); + return; + } + RegisterAllServiceRecords(m, rr); + return; + case regState_DeregPending: + // We are in DeregPending either because the service was deregistered from above or we handled + // a NAT/Target change before and sent the deregistration below. There are a few race conditions + // possible + // + // 1. We are handling a second NAT/Target change while the first dereg is in progress. It is possible + // that first dereg never made it through because there was no network connectivity e.g., disconnecting + // from network triggers this function due to a target change and later connecting to the network + // retriggers this function but the deregistration never made it through yet. Just fall through. + // If there is a target register otherwise deregister. + // + // 2. While we sent the dereg during a previous NAT/Target change, uDNS_DeregisterRecord gets + // called as part of service deregistration. When the response comes back, we call + // CompleteDeregistration rather than handle NAT/Target change because the record is in + // kDNSRecordTypeDeregistering state. + // + // 3. If the upper layer deregisters the service, we check for kDNSRecordTypeDeregistering both + // here in this function to avoid handling NAT/Target change and in hndlRecordUpdateReply to call + // CompleteDeregistration instead of handling NAT/Target change. Hence, we are not concerned + // about that case here. + // + // We just handle case (1) by falling through + case regState_Pending: + case regState_Refresh: + case regState_Registered: + // target or nat changed. deregister service. upon completion, we'll look for a new target + rr->SRVChanged = mDNStrue; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + if (newtarget->c[0]) + { + LogInfo("UpdateOneSRVRecord: SRV record changed for service %##s, registering with new target %##s", + rr->resrec.name->c, newtarget->c); + rr->state = regState_Pending; + } + else + { + LogInfo("UpdateOneSRVRecord: SRV record changed for service %##s de-registering", rr->resrec.name->c); + rr->state = regState_DeregPending; + UpdateAllServiceRecords(m, rr, mDNSfalse); + } + return; + case regState_Unregistered: + default: LogMsg("UpdateOneSRVRecord: Unknown state %d for %##s", rr->state, rr->resrec.name->c); + } +} -Revision 1.87 2004/09/22 00:41:59 cheshire -Move tcp connection status codes into the legal range allocated for mDNS use +mDNSexport void UpdateAllSRVRecords(mDNS *m) +{ + m->NextSRVUpdate = 0; + LogInfo("UpdateAllSRVRecords %d", m->SleepState); -Revision 1.86 2004/09/21 23:40:11 ksekar -<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure + if (m->CurrentRecord) + LogMsg("UpdateAllSRVRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rptr = m->CurrentRecord; + m->CurrentRecord = m->CurrentRecord->next; + if (AuthRecord_uDNS(rptr) && rptr->resrec.rrtype == kDNSType_SRV) + UpdateOneSRVRecord(m, rptr); + } +} -Revision 1.85 2004/09/21 22:38:27 ksekar -<rdar://problem/3810286> PrimaryIP type uninitialized +// Forward reference: AdvertiseHostname references HostnameCallback, and HostnameCallback calls AdvertiseHostname +mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); -Revision 1.84 2004/09/18 00:30:39 cheshire -<rdar://problem/3806643> Infinite loop in CheckServiceRegistrations +// Called in normal client context (lock not held) +mDNSlocal void hostnameGetPublicAddressCallback(mDNS *m, NATTraversalInfo *n) +{ + HostnameInfo *h = (HostnameInfo *)n->clientContext; -Revision 1.83 2004/09/17 00:31:51 cheshire -For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + if (!h) { LogMsg("RegisterHostnameRecord: registration cancelled"); return; } -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 + if (!n->Result) + { + if (mDNSIPv4AddressIsZero(n->ExternalAddress) || mDNSv4AddrIsRFC1918(&n->ExternalAddress)) return; -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 + if (h->arv4.resrec.RecordType) + { + if (mDNSSameIPv4Address(h->arv4.resrec.rdata->u.ipv4, n->ExternalAddress)) return; // If address unchanged, do nothing + LogInfo("Updating hostname %p %##s IPv4 from %.4a to %.4a (NAT gateway's external address)",n, + h->arv4.resrec.name->c, &h->arv4.resrec.rdata->u.ipv4, &n->ExternalAddress); + mDNS_Deregister(m, &h->arv4); // mStatus_MemFree callback will re-register with new address + } + else + { + LogInfo("Advertising hostname %##s IPv4 %.4a (NAT gateway's external address)", h->arv4.resrec.name->c, &n->ExternalAddress); + h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique; + h->arv4.resrec.rdata->u.ipv4 = n->ExternalAddress; + mDNS_Register(m, &h->arv4); + } + } +} -Revision 1.80 2004/09/16 01:58:21 cheshire -Fix compiler warnings +// register record or begin NAT traversal +mDNSlocal void AdvertiseHostname(mDNS *m, HostnameInfo *h) +{ + if (!mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4) && h->arv4.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, HostnameCallback, h); + AssignDomainName(&h->arv4.namestorage, &h->fqdn); + h->arv4.resrec.rdata->u.ipv4 = m->AdvertisedV4.ip.v4; + h->arv4.state = regState_Unregistered; + if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4)) + { + // If we already have a NAT query active, stop it and restart it to make sure we get another callback + if (h->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &h->natinfo); + h->natinfo.Protocol = 0; + h->natinfo.IntPort = zeroIPPort; + h->natinfo.RequestedPort = zeroIPPort; + h->natinfo.NATLease = 0; + h->natinfo.clientCallback = hostnameGetPublicAddressCallback; + h->natinfo.clientContext = h; + mDNS_StartNATOperation_internal(m, &h->natinfo); + } + else + { + LogInfo("Advertising hostname %##s IPv4 %.4a", h->arv4.resrec.name->c, &m->AdvertisedV4.ip.v4); + h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique; + mDNS_Register_internal(m, &h->arv4); + } + } -Revision 1.79 2004/09/16 00:24:48 cheshire -<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() + if (!mDNSIPv6AddressIsZero(m->AdvertisedV6.ip.v6) && h->arv6.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, HostnameCallback, h); + AssignDomainName(&h->arv6.namestorage, &h->fqdn); + h->arv6.resrec.rdata->u.ipv6 = m->AdvertisedV6.ip.v6; + h->arv6.state = regState_Unregistered; + LogInfo("Advertising hostname %##s IPv6 %.16a", h->arv6.resrec.name->c, &m->AdvertisedV6.ip.v6); + mDNS_Register_internal(m, &h->arv6); + } +} -Revision 1.78 2004/09/15 01:16:57 ksekar -<rdar://problem/3797598> mDNSResponder printing too many messages +mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + HostnameInfo *hi = (HostnameInfo *)rr->RecordContext; -Revision 1.77 2004/09/14 23:27:47 cheshire -Fix compile errors + if (result == mStatus_MemFree) + { + if (hi) + { + // If we're still in the Hostnames list, update to new address + HostnameInfo *i; + LogInfo("HostnameCallback: Got mStatus_MemFree for %p %p %s", hi, rr, ARDisplayString(m, rr)); + for (i = m->Hostnames; i; i = i->next) + if (rr == &i->arv4 || rr == &i->arv6) + { mDNS_Lock(m); AdvertiseHostname(m, i); mDNS_Unlock(m); return; } + + // Else, we're not still in the Hostnames list, so free the memory + if (hi->arv4.resrec.RecordType == kDNSRecordTypeUnregistered && + hi->arv6.resrec.RecordType == kDNSRecordTypeUnregistered) + { + if (hi->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &hi->natinfo); + hi->natinfo.clientContext = mDNSNULL; + mDNSPlatformMemFree(hi); // free hi when both v4 and v6 AuthRecs deallocated + } + } + return; + } -Revision 1.76 2004/09/14 22:22:00 ksekar -<rdar://problem/3800920> Legacy browses broken against some BIND versions + if (result) + { + // don't unlink or free - we can retry when we get a new address/router + if (rr->resrec.rrtype == kDNSType_A) + LogMsg("HostnameCallback: Error %d for registration of %##s IP %.4a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv4); + else + LogMsg("HostnameCallback: Error %d for registration of %##s IP %.16a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv6); + if (!hi) { mDNSPlatformMemFree(rr); return; } + if (rr->state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!"); -Revision 1.75 2004/09/03 19:23:05 ksekar -<rdar://problem/3788460>: Need retransmission mechanism for wide-area service registrations + if (hi->arv4.state == regState_Unregistered && + hi->arv6.state == regState_Unregistered) + { + // only deliver status if both v4 and v6 fail + rr->RecordContext = (void *)hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; + } + return; + } -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. + // register any pending services that require a target + mDNS_Lock(m); + m->NextSRVUpdate = NonZeroTime(m->timenow); + mDNS_Unlock(m); + + // Deliver success to client + if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; } + if (rr->resrec.rrtype == kDNSType_A) + LogInfo("Registered hostname %##s IP %.4a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv4); + else + LogInfo("Registered hostname %##s IP %.16a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv6); + + rr->RecordContext = (void *)hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; +} + +mDNSlocal void FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + const domainname *pktname = &answer->rdata->u.name; + domainname *storedname = &m->StaticHostname; + HostnameInfo *h = m->Hostnames; + + (void)question; + + if (answer->rdlength != 0) + LogInfo("FoundStaticHostname: question %##s -> answer %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "ADD" : "RMV"); + else + LogInfo("FoundStaticHostname: question %##s -> answer NULL (%s)", question->qname.c, AddRecord ? "ADD" : "RMV"); + + if (AddRecord && answer->rdlength != 0 && !SameDomainName(pktname, storedname)) + { + AssignDomainName(storedname, pktname); + while (h) + { + if (h->arv4.state == regState_Pending || h->arv4.state == regState_NATMap || h->arv6.state == regState_Pending) + { + // if we're in the process of registering a dynamic hostname, delay SRV update so we don't have to reregister services if the dynamic name succeeds + m->NextSRVUpdate = NonZeroTime(m->timenow + 5 * mDNSPlatformOneSecond); + debugf("FoundStaticHostname: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + return; + } + h = h->next; + } + mDNS_Lock(m); + m->NextSRVUpdate = NonZeroTime(m->timenow); + mDNS_Unlock(m); + } + else if (!AddRecord && SameDomainName(pktname, storedname)) + { + mDNS_Lock(m); + storedname->c[0] = 0; + m->NextSRVUpdate = NonZeroTime(m->timenow); + mDNS_Unlock(m); + } +} -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 +// Called with lock held +mDNSlocal void GetStaticHostname(mDNS *m) +{ + char buf[MAX_REVERSE_MAPPING_NAME_V4]; + DNSQuestion *q = &m->ReverseMap; + mDNSu8 *ip = m->AdvertisedV4.ip.v4.b; + mStatus err; -Revision 1.72 2004/09/01 03:59:29 ksekar -<rdar://problem/3783453>: Conditionally compile out uDNS code on Windows + if (m->ReverseMap.ThisQInterval != -1) return; // already running + if (mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4)) return; -Revision 1.71 2004/08/27 17:51:53 ksekar -Replaced unnecessary LogMsg with debugf. + mDNSPlatformMemZero(q, sizeof(*q)); + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", ip[3], ip[2], ip[1], ip[0]); + if (!MakeDomainNameFromDNSNameString(&q->qname, buf)) { LogMsg("Error: GetStaticHostname - bad name %s", buf); return; } -Revision 1.70 2004/08/25 00:37:27 ksekar -<rdar://problem/3774635>: Cleanup DynDNS hostname registration code + q->InterfaceID = mDNSInterface_Any; + q->flags = 0; + q->Target = zeroAddr; + q->qtype = kDNSType_PTR; + q->qclass = kDNSClass_IN; + q->LongLived = mDNSfalse; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = mDNSfalse; + q->ReturnIntermed = mDNStrue; + q->SuppressUnusable = mDNSfalse; + q->DenyOnCellInterface = mDNSfalse; + q->DenyOnExpInterface = mDNSfalse; + q->SearchListIndex = 0; + q->AppendSearchDomains = 0; + q->RetryWithSearchDomains = mDNSfalse; + q->TimeoutQuestion = 0; + q->WakeOnResolve = 0; + q->UseBackgroundTrafficClass = mDNSfalse; + q->ValidationRequired = 0; + q->ValidatingResponse = 0; + q->ProxyQuestion = 0; + q->qnameOrig = mDNSNULL; + q->AnonInfo = mDNSNULL; + q->pid = mDNSPlatformGetPID(); + q->QuestionCallback = FoundStaticHostname; + q->QuestionContext = mDNSNULL; -Revision 1.69 2004/08/18 17:35:41 ksekar -<rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways + LogInfo("GetStaticHostname: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + err = mDNS_StartQuery_internal(m, q); + if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err); +} -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 +mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext) +{ + HostnameInfo **ptr = &m->Hostnames; -Revision 1.67 2004/08/13 23:46:58 cheshire -"asyncronous" -> "asynchronous" + LogInfo("mDNS_AddDynDNSHostName %##s", fqdn); -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 + while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next; + if (*ptr) { LogMsg("DynDNSHostName %##s already in list", fqdn->c); return; } -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.) + // allocate and format new address record + *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); + if (!*ptr) { LogMsg("ERROR: mDNS_AddDynDNSHostName - malloc"); return; } -Revision 1.64 2004/08/13 23:01:05 cheshire -Use platform-independent mDNSNULL instead of NULL + mDNSPlatformMemZero(*ptr, sizeof(**ptr)); + AssignDomainName(&(*ptr)->fqdn, fqdn); + (*ptr)->arv4.state = regState_Unregistered; + (*ptr)->arv6.state = regState_Unregistered; + (*ptr)->StatusCallback = StatusCallback; + (*ptr)->StatusContext = StatusContext; -Revision 1.63 2004/08/12 00:32:36 ksekar -<rdar://problem/3759567>: LLQ Refreshes never terminate if unanswered + AdvertiseHostname(m, *ptr); +} -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 +mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) +{ + HostnameInfo **ptr = &m->Hostnames; -Revision 1.61 2004/07/30 17:40:06 ksekar -<rdar://problem/3739115>: TXT Record updates not available for wide-area services + LogInfo("mDNS_RemoveDynDNSHostName %##s", fqdn); -Revision 1.60 2004/07/29 19:40:05 ksekar -NATPMP Support - minor fixes and cleanup + while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next; + if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c); + else + { + HostnameInfo *hi = *ptr; + // We do it this way because, if we have no active v6 record, the "mDNS_Deregister_internal(m, &hi->arv4);" + // below could free the memory, and we have to make sure we don't touch hi fields after that. + mDNSBool f4 = hi->arv4.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv4.state != regState_Unregistered; + mDNSBool f6 = hi->arv6.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv6.state != regState_Unregistered; + if (f4) LogInfo("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn); + if (f6) LogInfo("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn); + *ptr = (*ptr)->next; // unlink + if (f4) mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal); + if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal); + // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback + } + mDNS_CheckLock(m); + m->NextSRVUpdate = NonZeroTime(m->timenow); +} -Revision 1.59 2004/07/29 19:27:15 ksekar -NATPMP Support - minor fixes and cleanup +// Currently called without holding the lock +// Maybe we should change that? +mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router) +{ + mDNSBool v4Changed, v6Changed, RouterChanged; + mDNSv6Addr v6; -Revision 1.58 2004/07/27 07:35:38 shersche -fix syntax error, variables declared in the middle of a block + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_SetPrimaryInterfaceInfo: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); -Revision 1.57 2004/07/26 22:49:30 ksekar -<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client + if (v4addr && v4addr->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo v4 address - incorrect type. Discarding. %#a", v4addr); return; } + if (v6addr && v6addr->type != mDNSAddrType_IPv6) { LogMsg("mDNS_SetPrimaryInterfaceInfo v6 address - incorrect type. Discarding. %#a", v6addr); return; } + if (router && router->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-v4 router. Discarding. %#a", router); return; } -Revision 1.56 2004/07/26 19:14:44 ksekar -<rdar://problem/3737814>: 8A210: mDNSResponder crashed in startLLQHandshakeCallback + mDNS_Lock(m); -Revision 1.55 2004/07/15 19:01:33 ksekar -<rdar://problem/3681029>: Check for incorrect time comparisons + v4Changed = !mDNSSameIPv4Address(m->AdvertisedV4.ip.v4, v4addr ? v4addr->ip.v4 : zerov4Addr); + if (v6addr) + v6 = v6addr->ip.v6; + else + v6 = zerov6Addr; + v6Changed = !mDNSSameIPv6Address(m->AdvertisedV6.ip.v6, v6); + RouterChanged = !mDNSSameIPv4Address(m->Router.ip.v4, router ? router->ip.v4 : zerov4Addr); -Revision 1.54 2004/06/22 02:10:53 ksekar -<rdar://problem/3705433>: Lighthouse failure causes packet flood to DNS + if (v4addr && (v4Changed || RouterChanged)) + debugf("mDNS_SetPrimaryInterfaceInfo: address changed from %#a to %#a", &m->AdvertisedV4, v4addr); -Revision 1.53 2004/06/17 20:49:09 ksekar -<rdar://problem/3690436>: mDNSResponder crash while location cycling + if (v4addr) m->AdvertisedV4 = *v4addr;else m->AdvertisedV4.ip.v4 = zerov4Addr; + if (v6addr) m->AdvertisedV6 = *v6addr;else m->AdvertisedV6.ip.v6 = zerov6Addr; + if (router) m->Router = *router;else m->Router.ip.v4 = zerov4Addr; + // setting router to zero indicates that nat mappings must be reestablished when router is reset -Revision 1.52 2004/06/17 01:13:11 ksekar -<rdar://problem/3696616>: polling interval too short + if (v4Changed || RouterChanged || v6Changed) + { + HostnameInfo *i; + LogInfo("mDNS_SetPrimaryInterfaceInfo: %s%s%s%#a %#a %#a", + v4Changed ? "v4Changed " : "", + RouterChanged ? "RouterChanged " : "", + v6Changed ? "v6Changed " : "", v4addr, v6addr, router); -Revision 1.51 2004/06/10 04:36:44 cheshire -Fix compiler warning + for (i = m->Hostnames; i; i = i->next) + { + LogInfo("mDNS_SetPrimaryInterfaceInfo updating host name registrations for %##s", i->fqdn.c); -Revision 1.50 2004/06/10 00:55:13 ksekar -<rdar://problem/3686213>: crash on network reconnect + if (i->arv4.resrec.RecordType > kDNSRecordTypeDeregistering && + !mDNSSameIPv4Address(i->arv4.resrec.rdata->u.ipv4, m->AdvertisedV4.ip.v4)) + { + LogInfo("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv4)); + mDNS_Deregister_internal(m, &i->arv4, mDNS_Dereg_normal); + } -Revision 1.49 2004/06/10 00:10:50 ksekar -<rdar://problem/3686174>: Infinite Loop in uDNS_Execute() + if (i->arv6.resrec.RecordType > kDNSRecordTypeDeregistering && + !mDNSSameIPv6Address(i->arv6.resrec.rdata->u.ipv6, m->AdvertisedV6.ip.v6)) + { + LogInfo("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv6)); + mDNS_Deregister_internal(m, &i->arv6, mDNS_Dereg_normal); + } -Revision 1.48 2004/06/09 20:03:37 ksekar -<rdar://problem/3686163>: Incorrect copying of resource record in deregistration + // AdvertiseHostname will only register new address records. + // For records still in the process of deregistering it will ignore them, and let the mStatus_MemFree callback handle them. + AdvertiseHostname(m, i); + } -Revision 1.47 2004/06/09 03:48:28 ksekar -<rdar://problem/3685226>: nameserver address fails with prod. Lighthouse server + if (v4Changed || RouterChanged) + { + // If we have a non-zero IPv4 address, we should try immediately to see if we have a NAT gateway + // If we have no IPv4 address, we don't want to be in quite such a hurry to report failures to our clients + // <rdar://problem/6935929> Sleeping server sometimes briefly disappears over Back to My Mac after it wakes up + mDNSu32 waitSeconds = v4addr ? 0 : 5; + NATTraversalInfo *n; + m->ExtAddress = zerov4Addr; + m->LastNATMapResultCode = NATErr_None; + + RecreateNATMappings(m, mDNSPlatformOneSecond * waitSeconds); + + for (n = m->NATTraversals; n; n=n->next) + n->NewAddress = zerov4Addr; + + LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: recreating NAT mappings in %d seconds", + v4Changed ? " v4Changed" : "", + RouterChanged ? " RouterChanged" : "", + waitSeconds); + } -Revision 1.46 2004/06/09 01:44:30 ksekar -<rdar://problem/3681378> reworked Cache Record copy code + if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, &m->ReverseMap); + m->StaticHostname.c[0] = 0; -Revision 1.45 2004/06/08 18:54:47 ksekar -<rdar://problem/3681378>: mDNSResponder leaks after exploring in Printer Setup Utility + m->NextSRVUpdate = NonZeroTime(m->timenow); -Revision 1.44 2004/06/05 00:33:51 cheshire -<rdar://problem/3681029>: Check for incorrect time comparisons +#if APPLE_OSX_mDNSResponder + if (RouterChanged) uuid_generate(m->asl_uuid); + UpdateAutoTunnelDomainStatuses(m); +#endif + } -Revision 1.43 2004/06/05 00:14:44 cheshire -Fix signed/unsigned and other compiler warnings + mDNS_Unlock(m); +} -Revision 1.42 2004/06/04 22:36:16 ksekar -Properly set u->nextevent in uDNS_Execute +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Incoming Message Processing +#endif -Revision 1.41 2004/06/04 08:58:29 ksekar -<rdar://problem/3668624>: Keychain integration for secure dynamic update +mDNSlocal mStatus ParseTSIGError(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const domainname *const displayname) +{ + const mDNSu8 *ptr; + mStatus err = mStatus_NoError; + int i; -Revision 1.40 2004/06/03 03:09:58 ksekar -<rdar://problem/3668626>: Garbage Collection for Dynamic Updates + ptr = LocateAdditionals(msg, end); + if (!ptr) goto finish; -Revision 1.39 2004/06/01 23:46:50 ksekar -<rdar://problem/3675149>: DynDNS: dynamically look up LLQ/Update ports + for (i = 0; i < msg->h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); + if (!ptr) goto finish; + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_TSIG) + { + mDNSu32 macsize; + mDNSu8 *rd = m->rec.r.resrec.rdata->u.data; + mDNSu8 *rdend = rd + m->rec.r.resrec.rdlength; + int alglen = DomainNameLengthLimit(&m->rec.r.resrec.rdata->u.name, rdend); + if (alglen > MAX_DOMAIN_NAME) goto finish; + rd += alglen; // algorithm name + if (rd + 6 > rdend) goto finish; + rd += 6; // 48-bit timestamp + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + rd += sizeof(mDNSOpaque16); // fudge + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + macsize = mDNSVal16(*(mDNSOpaque16 *)rd); + rd += sizeof(mDNSOpaque16); // MAC size + if (rd + macsize > rdend) goto finish; + rd += macsize; + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + rd += sizeof(mDNSOpaque16); // orig id + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + err = mDNSVal16(*(mDNSOpaque16 *)rd); // error code + + if (err == TSIG_ErrBadSig) { LogMsg("%##s: bad signature", displayname->c); err = mStatus_BadSig; } + else if (err == TSIG_ErrBadKey) { LogMsg("%##s: bad key", displayname->c); err = mStatus_BadKey; } + else if (err == TSIG_ErrBadTime) { LogMsg("%##s: bad time", displayname->c); err = mStatus_BadTime; } + else if (err) { LogMsg("%##s: unknown tsig error %d", displayname->c, err); err = mStatus_UnknownErr; } + goto finish; + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } -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 +finish: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + return err; +} -Revision 1.37 2004/05/28 23:42:37 ksekar -<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) +mDNSlocal mStatus checkUpdateResult(mDNS *const m, const domainname *const displayname, const mDNSu8 rcode, const DNSMessage *const msg, const mDNSu8 *const end) +{ + (void)msg; // currently unused, needed for TSIG errors + if (!rcode) return mStatus_NoError; + else if (rcode == kDNSFlag1_RC_YXDomain) + { + debugf("name in use: %##s", displayname->c); + return mStatus_NameConflict; + } + else if (rcode == kDNSFlag1_RC_Refused) + { + LogMsg("Update %##s refused", displayname->c); + return mStatus_Refused; + } + else if (rcode == kDNSFlag1_RC_NXRRSet) + { + LogMsg("Reregister refused (NXRRSET): %##s", displayname->c); + return mStatus_NoSuchRecord; + } + else if (rcode == kDNSFlag1_RC_NotAuth) + { + // TSIG errors should come with FormErr as per RFC 2845, but BIND 9 sends them with NotAuth so we look here too + mStatus tsigerr = ParseTSIGError(m, msg, end, displayname); + if (!tsigerr) + { + LogMsg("Permission denied (NOAUTH): %##s", displayname->c); + return mStatus_UnknownErr; + } + else return tsigerr; + } + else if (rcode == kDNSFlag1_RC_FormErr) + { + mStatus tsigerr = ParseTSIGError(m, msg, end, displayname); + if (!tsigerr) + { + LogMsg("Format Error: %##s", displayname->c); + return mStatus_UnknownErr; + } + else return tsigerr; + } + else + { + LogMsg("Update %##s failed with rcode %d", displayname->c, rcode); + return mStatus_UnknownErr; + } +} + +// We add three Additional Records for unicast resource record registrations +// which is a function of AuthInfo and AutoTunnel properties +mDNSlocal mDNSu32 RRAdditionalSize(mDNS *const m, DomainAuthInfo *AuthInfo) +{ + mDNSu32 leaseSize, hinfoSize, tsigSize; + mDNSu32 rr_base_size = 10; // type (2) class (2) TTL (4) rdlength (2) + + // OPT RR : Emptyname(.) + base size + rdataOPT + leaseSize = 1 + rr_base_size + sizeof(rdataOPT); + + // HINFO: Resource Record Name + base size + RDATA + // HINFO is added only for autotunnels + hinfoSize = 0; + if (AuthInfo && AuthInfo->AutoTunnel) + hinfoSize = (m->hostlabel.c[0] + 1) + DomainNameLength(&AuthInfo->domain) + + rr_base_size + (2 + m->HIHardware.c[0] + m->HISoftware.c[0]); + + //TSIG: Resource Record Name + base size + RDATA + // RDATA: + // Algorithm name: hmac-md5.sig-alg.reg.int (8+7+3+3 + 5 bytes for length = 26 bytes) + // Time: 6 bytes + // Fudge: 2 bytes + // Mac Size: 2 bytes + // Mac: 16 bytes + // ID: 2 bytes + // Error: 2 bytes + // Len: 2 bytes + // Total: 58 bytes + tsigSize = 0; + if (AuthInfo) tsigSize = DomainNameLength(&AuthInfo->keyname) + rr_base_size + 58; + + return (leaseSize + hinfoSize + tsigSize); +} + +//Note: Make sure that RREstimatedSize is updated accordingly if anything that is done here +//would modify rdlength/rdestimate +mDNSlocal mDNSu8* BuildUpdateMessage(mDNS *const m, mDNSu8 *ptr, AuthRecord *rr, mDNSu8 *limit) +{ + //If this record is deregistering, then just send the deletion record + if (rr->state == regState_DeregPending) + { + rr->expire = 0; // Indicate that we have no active registration any more + ptr = putDeletionRecordWithLimit(&m->omsg, ptr, &rr->resrec, limit); + if (!ptr) goto exit; + return ptr; + } -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 + // This is a common function to both sending an update in a group or individual + // records separately. Hence, we change the state here. + if (rr->state == regState_Registered) rr->state = regState_Refresh; + if (rr->state != regState_Refresh && rr->state != regState_UpdatePending) + rr->state = regState_Pending; -Revision 1.35 2004/05/07 23:01:04 ksekar -Cleaned up list traversal in deriveGoodbyes - removed unnecessary -conditional assignment. + // For Advisory records like e.g., _services._dns-sd, which is shared, don't send goodbyes as multiple + // host might be registering records and deregistering from one does not make sense + if (rr->resrec.RecordType != kDNSRecordTypeAdvisory) rr->RequireGoodbye = mDNStrue; -Revision 1.34 2004/05/05 18:26:12 ksekar -Periodically re-transmit questions if the send() fails. Include -internal questions in retransmission. + if ((rr->resrec.rrtype == kDNSType_SRV) && (rr->AutoTarget == Target_AutoHostAndNATMAP) && + !mDNSIPPortIsZero(rr->NATinfo.ExternalPort)) + { + rr->resrec.rdata->u.srv.port = rr->NATinfo.ExternalPort; + } -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. + if (rr->state == regState_UpdatePending) + { + // delete old RData + SetNewRData(&rr->resrec, rr->OrigRData, rr->OrigRDLen); + if (!(ptr = putDeletionRecordWithLimit(&m->omsg, ptr, &rr->resrec, limit))) goto exit; // delete old rdata -Revision 1.32 2004/05/05 17:32:18 ksekar -Prevent registration of loopback interface caused by removal of -Multicast flag in interface structure. + // add new RData + SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); + if (!(ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit))) goto exit; + } + else + { + if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->resrec.RecordType == kDNSRecordTypeVerified) + { + // KnownUnique : Delete any previous value + // For Unicast registrations, we don't verify that it is unique, but set to verified and hence we want to + // delete any previous value + ptr = putDeleteRRSetWithLimit(&m->omsg, ptr, rr->resrec.name, rr->resrec.rrtype, limit); + if (!ptr) goto exit; + } + else if (rr->resrec.RecordType != kDNSRecordTypeShared) + { + // For now don't do this, until we have the logic for intelligent grouping of individual records into logical service record sets + //ptr = putPrereqNameNotInUse(rr->resrec.name, &m->omsg, ptr, end); + if (!ptr) goto exit; + } -Revision 1.31 2004/05/05 17:05:02 ksekar -Use LargeCacheRecord structs when pulling records off packets + ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); + if (!ptr) goto exit; + } -Revision 1.30 2004/04/16 21:33:27 ksekar -Fixed bug in processing GetZoneData responses that do not use BIND formatting. + return ptr; +exit: + LogMsg("BuildUpdateMessage: Error formatting message for %s", ARDisplayString(m, rr)); + return mDNSNULL; +} -Revision 1.29 2004/04/15 20:03:13 ksekar -Clarified log message when pulling bad resource records off packet. +// Called with lock held +mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr) +{ + mDNSu8 *ptr = m->omsg.data; + mStatus err = mStatus_UnknownErr; + mDNSu8 *limit; + DomainAuthInfo *AuthInfo; -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. + // For the ability to register large TXT records, we limit the single record registrations + // to AbsoluteMaxDNSMessageData + limit = ptr + AbsoluteMaxDNSMessageData; -Revision 1.27 2004/04/14 23:09:28 ksekar -Support for TSIG signed dynamic updates. + AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + limit -= RRAdditionalSize(m, AuthInfo); -Revision 1.26 2004/04/14 19:36:05 ksekar -Fixed memory corruption error in deriveGoodbyes. + mDNS_CheckLock(m); -Revision 1.25 2004/04/14 04:07:11 ksekar -Fixed crash in IsActiveUnicastQuery(). Removed redundant checks in routine. + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) + { + // We never call this function when there is no zone information . Log a message if it ever happens. + LogMsg("SendRecordRegistration: No Zone information, should not happen %s", ARDisplayString(m, rr)); + return; + } -Revision 1.24 2004/04/08 09:41:40 bradley -Added const to AuthRecord in deadvertiseIfCallback to match callback typedef. + rr->updateid = mDNS_NewMessageID(m); + InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags); -Revision 1.23 2004/03/24 00:29:45 ksekar -Make it safe to call StopQuery in a unicast question callback + // set zone + ptr = putZone(&m->omsg, ptr, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto exit; -Revision 1.22 2004/03/19 10:11:09 bradley -Added AuthRecord * cast from umalloc for C++ builds. + if (!(ptr = BuildUpdateMessage(m, ptr, rr, limit))) goto exit; -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++. + if (rr->uselease) + { + ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit); + if (!ptr) goto exit; + } + if (rr->Private) + { + LogInfo("SendRecordRegistration TCP %p %s", rr->tcp, ARDisplayString(m, rr)); + if (rr->tcp) LogInfo("SendRecordRegistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr)); + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (!rr->nta) { LogMsg("SendRecordRegistration:Private:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } + rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->nta->Addr, rr->nta->Port, &rr->nta->Host, mDNSNULL, rr); + } + else + { + LogInfo("SendRecordRegistration UDP %s", ARDisplayString(m, rr)); + if (!rr->nta) { LogMsg("SendRecordRegistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } + err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); + if (err) debugf("ERROR: SendRecordRegistration - mDNSSendDNSMessage - %d", err); + } -Revision 1.20 2004/03/13 02:07:26 ksekar -<rdar://problem/3192546>: DynDNS: Dynamic update of service records + SetRecordRetry(m, rr, 0); + return; +exit: + LogMsg("SendRecordRegistration: Error formatting message for %s, disabling further updates", ARDisplayString(m, rr)); + // Disable this record from future updates + rr->state = regState_NoTarget; +} + +// Is the given record "rr" eligible for merging ? +mDNSlocal mDNSBool IsRecordMergeable(mDNS *const m, AuthRecord *rr, mDNSs32 time) +{ + DomainAuthInfo *info; + (void) m; //unused + // A record is eligible for merge, if the following properties are met. + // + // 1. uDNS Resource Record + // 2. It is time to send them now + // 3. It is in proper state + // 4. Update zone has been resolved + // 5. if DomainAuthInfo exists for the zone, it should not be soon deleted + // 6. Zone information is present + // 7. Update server is not zero + // 8. It has a non-null zone + // 9. It uses a lease option + // 10. DontMerge is not set + // + // Following code is implemented as separate "if" statements instead of one "if" statement + // is for better debugging purposes e.g., we know exactly what failed if debugging turned on. + + if (!AuthRecord_uDNS(rr)) return mDNSfalse; + + if (rr->LastAPTime + rr->ThisAPInterval - time > 0) + { debugf("IsRecordMergeable: Time %d not reached for %s", rr->LastAPTime + rr->ThisAPInterval - m->timenow, ARDisplayString(m, rr)); return mDNSfalse; } + + if (!rr->zone) return mDNSfalse; + + info = GetAuthInfoForName_internal(m, rr->zone); + + if (info && info->deltime && m->timenow - info->deltime >= 0) {debugf("IsRecordMergeable: Domain %##s will be deleted soon", info->domain.c); return mDNSfalse;} + + if (rr->state != regState_DeregPending && rr->state != regState_Pending && rr->state != regState_Registered && rr->state != regState_Refresh && rr->state != regState_UpdatePending) + { debugf("IsRecordMergeable: state %d not right %s", rr->state, ARDisplayString(m, rr)); return mDNSfalse; } + + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) return mDNSfalse; + + if (!rr->uselease) return mDNSfalse; + + if (rr->mState == mergeState_DontMerge) {debugf("IsRecordMergeable Dontmerge true %s", ARDisplayString(m, rr)); return mDNSfalse;} + debugf("IsRecordMergeable: Returning true for %s", ARDisplayString(m, rr)); + return mDNStrue; +} + +// Is the resource record "rr" eligible to merge to with "currentRR" ? +mDNSlocal mDNSBool AreRecordsMergeable(mDNS *const m, AuthRecord *currentRR, AuthRecord *rr, mDNSs32 time) +{ + // A record is eligible to merge with another record as long it is eligible for merge in itself + // and it has the same zone information as the other record + if (!IsRecordMergeable(m, rr, time)) return mDNSfalse; + + if (!SameDomainName(currentRR->zone, rr->zone)) + { debugf("AreRecordMergeable zone mismatch current rr Zone %##s, rr zone %##s", currentRR->zone->c, rr->zone->c); return mDNSfalse; } + + if (!mDNSSameIPv4Address(currentRR->nta->Addr.ip.v4, rr->nta->Addr.ip.v4)) return mDNSfalse; + + if (!mDNSSameIPPort(currentRR->nta->Port, rr->nta->Port)) return mDNSfalse; + + debugf("AreRecordsMergeable: Returning true for %s", ARDisplayString(m, rr)); + return mDNStrue; +} + +// If we can't build the message successfully because of problems in pre-computing +// the space, we disable merging for all the current records +mDNSlocal void RRMergeFailure(mDNS *const m) +{ + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + rr->mState = mergeState_DontMerge; + rr->SendRNow = mDNSNULL; + // Restarting the registration is much simpler than saving and restoring + // the exact time + ActivateUnicastRegistration(m, rr); + } +} -Revision 1.19 2004/03/13 01:57:33 ksekar -<rdar://problem/3192546>: DynDNS: Dynamic update of service records +mDNSlocal void SendGroupRRMessage(mDNS *const m, AuthRecord *anchorRR, mDNSu8 *ptr, DomainAuthInfo *info) +{ + mDNSu8 *limit; + if (!anchorRR) {debugf("SendGroupRRMessage: Could not merge records"); return;} -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. + if (info && info->AutoTunnel) limit = m->omsg.data + AbsoluteMaxDNSMessageData; + else limit = m->omsg.data + NormalMaxDNSMessageData; -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 + // This has to go in the additional section and hence need to be done last + ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit); + if (!ptr) + { + LogMsg("SendGroupRRMessage: ERROR: Could not put lease option, failing the group registration"); + // if we can't put the lease, we need to undo the merge + RRMergeFailure(m); + return; + } + if (anchorRR->Private) + { + if (anchorRR->tcp) debugf("SendGroupRRMessage: Disposing existing TCP connection for %s", ARDisplayString(m, anchorRR)); + if (anchorRR->tcp) { DisposeTCPConn(anchorRR->tcp); anchorRR->tcp = mDNSNULL; } + if (!anchorRR->nta) { LogMsg("SendGroupRRMessage:ERROR!! nta is NULL for %s", ARDisplayString(m, anchorRR)); return; } + anchorRR->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &anchorRR->nta->Addr, anchorRR->nta->Port, &anchorRR->nta->Host, mDNSNULL, anchorRR); + if (!anchorRR->tcp) LogInfo("SendGroupRRMessage: Cannot establish TCP connection for %s", ARDisplayString(m, anchorRR)); + else LogInfo("SendGroupRRMessage: Sent a group update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit); + } + else + { + mStatus err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &anchorRR->nta->Addr, anchorRR->nta->Port, mDNSNULL, info, mDNSfalse); + if (err) LogInfo("SendGroupRRMessage: Cannot send UDP message for %s", ARDisplayString(m, anchorRR)); + else LogInfo("SendGroupRRMessage: Sent a group UDP update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit); + } + return; +} + +// As we always include the zone information and the resource records contain zone name +// at the end, it will get compressed. Hence, we subtract zoneSize and add two bytes for +// the compression pointer +mDNSlocal mDNSu32 RREstimatedSize(AuthRecord *rr, int zoneSize) +{ + int rdlength; + + // Note: Estimation of the record size has to mirror the logic in BuildUpdateMessage, otherwise estimation + // would be wrong. Currently BuildUpdateMessage calls SetNewRData in UpdatePending case. Hence, we need + // to account for that here. Otherwise, we might under estimate the size. + if (rr->state == regState_UpdatePending) + // old RData that will be deleted + // new RData that will be added + rdlength = rr->OrigRDLen + rr->InFlightRDLen; + else + rdlength = rr->resrec.rdestimate; + + if (rr->state == regState_DeregPending) + { + debugf("RREstimatedSize: ResourceRecord %##s (%s), DomainNameLength %d, zoneSize %d, rdestimate %d", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), DomainNameLength(rr->resrec.name), zoneSize, rdlength); + return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + rdlength; + } -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 + // For SRV, TXT, AAAA etc. that are Unique/Verified, we also send a Deletion Record + if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->resrec.RecordType == kDNSRecordTypeVerified) + { + // Deletion Record: Resource Record Name + Base size (10) + 0 + // Record: Resource Record Name (Compressed = 2) + Base size (10) + rdestimate -Revision 1.15 2004/02/10 03:02:46 cheshire -Fix compiler warning + debugf("RREstimatedSize: ResourceRecord %##s (%s), DomainNameLength %d, zoneSize %d, rdestimate %d", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), DomainNameLength(rr->resrec.name), zoneSize, rdlength); + return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + 2 + 10 + rdlength; + } + else + { + return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + rdlength; + } +} + +mDNSlocal AuthRecord *MarkRRForSending(mDNS *const m) +{ + AuthRecord *rr; + AuthRecord *firstRR = mDNSNULL; + + // Look for records that needs to be sent in the next two seconds (MERGE_DELAY_TIME is set to 1 second). + // The logic is as follows. + // + // 1. Record 1 finishes getting zone data and its registration gets delayed by 1 second + // 2. Record 2 comes 0.1 second later, finishes getting its zone data and its registration is also delayed by + // 1 second which is now scheduled at 1.1 second + // + // By looking for 1 second into the future (m->timenow + MERGE_DELAY_TIME below does that) we have merged both + // of the above records. Note that we can't look for records too much into the future as this will affect the + // retry logic. The first retry is scheduled at 3 seconds. Hence, we should always look smaller than that. + // Anything more than one second will affect the first retry to happen sooner. + // + // Note: As a side effect of looking one second into the future to facilitate merging, the retries happen + // one second sooner. + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if (!firstRR) + { + if (!IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) continue; + firstRR = rr; + } + else if (!AreRecordsMergeable(m, firstRR, rr, m->timenow + MERGE_DELAY_TIME)) continue; -Revision 1.14 2004/02/06 23:04:19 ksekar -Basic Dynamic Update support via mDNS_Register (dissabled via -UNICAST_REGISTRATION #define) + if (rr->SendRNow) LogMsg("MarkRRForSending: Resourcerecord %s already marked for sending", ARDisplayString(m, rr)); + rr->SendRNow = uDNSInterfaceMark; + } -Revision 1.13 2004/02/03 22:15:01 ksekar -Fixed nameToAddr error check: don't abort state machine on nxdomain error. + // We parsed through all records and found something to send. The services/records might + // get registered at different times but we want the refreshes to be all merged and sent + // as one update. Hence, we accelerate some of the records so that they will sync up in + // the future. Look at the records excluding the ones that we have already sent in the + // previous pass. If it half way through its scheduled refresh/retransmit, merge them + // into this packet. + // + // Note that we only look at Registered/Refresh state to keep it simple. As we don't know + // whether the current update will fit into one or more packets, merging a resource record + // (which is in a different state) that has been scheduled for retransmit would trigger + // sending more packets. + if (firstRR) + { + int acc = 0; + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if ((rr->state != regState_Registered && rr->state != regState_Refresh) || + (rr->SendRNow == uDNSInterfaceMark) || + (!AreRecordsMergeable(m, firstRR, rr, m->timenow + rr->ThisAPInterval/2))) + continue; + rr->SendRNow = uDNSInterfaceMark; + acc++; + } + if (acc) LogInfo("MarkRRForSending: Accelereated %d records", acc); + } + return firstRR; +} + +mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m) +{ + mDNSOpaque16 msgid; + mDNSs32 spaceleft = 0; + mDNSs32 zoneSize, rrSize; + mDNSu8 *oldnext; // for debugging + mDNSu8 *next = m->omsg.data; + AuthRecord *rr; + AuthRecord *anchorRR = mDNSNULL; + int nrecords = 0; + AuthRecord *startRR = m->ResourceRecords; + mDNSu8 *limit = mDNSNULL; + DomainAuthInfo *AuthInfo = mDNSNULL; + mDNSBool sentallRecords = mDNStrue; + + + // We try to fit as many ResourceRecords as possible in AbsoluteNormal/MaxDNSMessageData. Before we start + // putting in resource records, we need to reserve space for a few things. Every group/packet should + // have the following. + // + // 1) Needs space for the Zone information (which needs to be at the beginning) + // 2) Additional section MUST have space for lease option, HINFO and TSIG option (which needs to + // to be at the end) + // + // In future we need to reserve space for the pre-requisites which also goes at the beginning. + // To accomodate pre-requisites in the future, first we walk the whole list marking records + // that can be sent in this packet and computing the space needed for these records. + // For TXT and SRV records, we delete the previous record if any by sending the same + // resource record with ANY RDATA and zero rdlen. Hence, we need to have space for both of them. + + while (startRR) + { + AuthInfo = mDNSNULL; + anchorRR = mDNSNULL; + nrecords = 0; + zoneSize = 0; + for (rr = startRR; rr; rr = rr->next) + { + if (rr->SendRNow != uDNSInterfaceMark) continue; -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. + rr->SendRNow = mDNSNULL; -Revision 1.11 2004/01/30 02:12:30 ksekar -Changed uDNS_ReceiveMsg() to correctly return void. + if (!anchorRR) + { + AuthInfo = GetAuthInfoForName_internal(m, rr->zone); + + // Though we allow single record registrations for UDP to be AbsoluteMaxDNSMessageData (See + // SendRecordRegistration) to handle large TXT records, to avoid fragmentation we limit UDP + // message to NormalMaxDNSMessageData + if (AuthInfo && AuthInfo->AutoTunnel) spaceleft = AbsoluteMaxDNSMessageData; + else spaceleft = NormalMaxDNSMessageData; + + next = m->omsg.data; + spaceleft -= RRAdditionalSize(m, AuthInfo); + if (spaceleft <= 0) + { + LogMsg("SendGroupUpdates: ERROR!!: spaceleft is zero at the beginning"); + RRMergeFailure(m); + return mDNSfalse; + } + limit = next + spaceleft; + + // Build the initial part of message before putting in the other records + msgid = mDNS_NewMessageID(m); + InitializeDNSMessage(&m->omsg.h, msgid, UpdateReqFlags); + + // We need zone information at the beginning of the packet. Length: ZNAME, ZTYPE(2), ZCLASS(2) + // zone has to be non-NULL for a record to be mergeable, hence it is safe to set/ examine zone + //without checking for NULL. + zoneSize = DomainNameLength(rr->zone) + 4; + spaceleft -= zoneSize; + if (spaceleft <= 0) + { + LogMsg("SendGroupUpdates: ERROR no space for zone information, disabling merge"); + RRMergeFailure(m); + return mDNSfalse; + } + next = putZone(&m->omsg, next, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!next) + { + LogMsg("SendGroupUpdates: ERROR! Cannot put zone, disabling merge"); + RRMergeFailure(m); + return mDNSfalse; + } + anchorRR = rr; + } -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. + rrSize = RREstimatedSize(rr, zoneSize - 4); -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. + if ((spaceleft - rrSize) < 0) + { + // If we can't fit even a single message, skip it, it will be sent separately + // in CheckRecordUpdates + if (!nrecords) + { + LogInfo("SendGroupUpdates: Skipping message %s, spaceleft %d, rrSize %d", ARDisplayString(m, rr), spaceleft, rrSize); + // Mark this as not sent so that the caller knows about it + rr->SendRNow = uDNSInterfaceMark; + // We need to remove the merge delay so that we can send it immediately + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + rr = rr->next; + anchorRR = mDNSNULL; + sentallRecords = mDNSfalse; + } + else + { + LogInfo("SendGroupUpdates:1: Parsed %d records and sending using %s, spaceleft %d, rrSize %d", nrecords, ARDisplayString(m, anchorRR), spaceleft, rrSize); + SendGroupRRMessage(m, anchorRR, next, AuthInfo); + } + break; // breaks out of for loop + } + spaceleft -= rrSize; + oldnext = next; + LogInfo("SendGroupUpdates: Building a message with resource record %s, next %p, state %d, ttl %d", ARDisplayString(m, rr), next, rr->state, rr->resrec.rroriginalttl); + if (!(next = BuildUpdateMessage(m, next, rr, limit))) + { + // We calculated the space and if we can't fit in, we had some bug in the calculation, + // disable merge completely. + LogMsg("SendGroupUpdates: ptr NULL while building message with %s", ARDisplayString(m, rr)); + RRMergeFailure(m); + return mDNSfalse; + } + // If our estimate was higher, adjust to the actual size + if ((next - oldnext) > rrSize) + LogMsg("SendGroupUpdates: ERROR!! Record size estimation is wrong for %s, Estimate %d, Actual %d, state %d", ARDisplayString(m, rr), rrSize, next - oldnext, rr->state); + else { spaceleft += rrSize; spaceleft -= (next - oldnext); } + + nrecords++; + // We could have sent an update earlier with this "rr" as anchorRR for which we never got a response. + // To preserve ordering, we blow away the previous connection before sending this. + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL;} + rr->updateid = msgid; + + // By setting the retry time interval here, we will not be looking at these records + // again when we return to CheckGroupRecordUpdates. + SetRecordRetry(m, rr, 0); + } + // Either we have parsed all the records or stopped at "rr" above due to lack of space + startRR = rr; + } -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. + if (anchorRR) + { + LogInfo("SendGroupUpdates: Parsed %d records and sending using %s", nrecords, ARDisplayString(m, anchorRR)); + SendGroupRRMessage(m, anchorRR, next, AuthInfo); + } + return sentallRecords; +} + +// Merge the record registrations and send them as a group only if they +// have same DomainAuthInfo and hence the same key to put the TSIG +mDNSlocal void CheckGroupRecordUpdates(mDNS *const m) +{ + AuthRecord *rr, *nextRR; + // Keep sending as long as there is at least one record to be sent + while (MarkRRForSending(m)) + { + if (!SendGroupUpdates(m)) + { + // if everything that was marked was not sent, send them out individually + for (rr = m->ResourceRecords; rr; rr = nextRR) + { + // SendRecordRegistrtion might delete the rr from list, hence + // dereference nextRR before calling the function + nextRR = rr->next; + if (rr->SendRNow == uDNSInterfaceMark) + { + // Any records marked for sending should be eligible to be sent out + // immediately. Just being cautious + if (rr->LastAPTime + rr->ThisAPInterval - m->timenow > 0) + { LogMsg("CheckGroupRecordUpdates: ERROR!! Resourcerecord %s not ready", ARDisplayString(m, rr)); continue; } + rr->SendRNow = mDNSNULL; + SendRecordRegistration(m, rr); + } + } + } + } -Revision 1.7 2004/01/27 20:15:22 cheshire -<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 + debugf("CheckGroupRecordUpdates: No work, returning"); + return; +} + +mDNSlocal void hndlSRVChanged(mDNS *const m, AuthRecord *rr) +{ + // Reevaluate the target always as NAT/Target could have changed while + // we were registering/deeregistering + domainname *dt; + const domainname *target = GetServiceTarget(m, rr); + if (!target || target->c[0] == 0) + { + // we don't have a target, if we just derregistered, then we don't have to do anything + if (rr->state == regState_DeregPending) + { + LogInfo("hndlSRVChanged: SRVChanged, No Target, SRV Deregistered for %##s, state %d", rr->resrec.name->c, + rr->state); + rr->SRVChanged = mDNSfalse; + dt = GetRRDomainNameTarget(&rr->resrec); + if (dt) dt->c[0] = 0; + rr->state = regState_NoTarget; // Wait for the next target change + rr->resrec.rdlength = rr->resrec.rdestimate = 0; + return; + } -Revision 1.6 2004/01/24 23:47:17 cheshire -Use mDNSOpaque16fromIntVal() instead of shifting and masking + // we don't have a target, if we just registered, we need to deregister + if (rr->state == regState_Pending) + { + LogInfo("hndlSRVChanged: SRVChanged, No Target, Deregistering again %##s, state %d", rr->resrec.name->c, rr->state); + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + rr->state = regState_DeregPending; + return; + } + LogInfo("hndlSRVChanged: Not in DeregPending or RegPending state %##s, state %d", rr->resrec.name->c, rr->state); + } + else + { + // If we were in registered state and SRV changed to NULL, we deregister and come back here + // if we have a target, we need to register again. + // + // if we just registered check to see if it is same. If it is different just re-register the + // SRV and its assoicated records + // + // UpdateOneSRVRecord takes care of re-registering all service records + if ((rr->state == regState_DeregPending) || + (rr->state == regState_Pending && !SameDomainName(target, &rr->resrec.rdata->u.srv.target))) + { + dt = GetRRDomainNameTarget(&rr->resrec); + if (dt) dt->c[0] = 0; + rr->state = regState_NoTarget; // NoTarget will allow us to pick up new target OR nat traversal state + rr->resrec.rdlength = rr->resrec.rdestimate = 0; + LogInfo("hndlSRVChanged: SRVChanged, Valid Target %##s, Registering all records for %##s, state %d", + target->c, rr->resrec.name->c, rr->state); + rr->SRVChanged = mDNSfalse; + UpdateOneSRVRecord(m, rr); + return; + } + // Target did not change while this record was registering. Hence, we go to + // Registered state - the state we started from. + if (rr->state == regState_Pending) rr->state = regState_Registered; + } -Revision 1.5 2004/01/24 04:59:15 cheshire -Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + rr->SRVChanged = mDNSfalse; +} -Revision 1.4 2004/01/24 04:19:26 cheshire -Restore overwritten checkin 1.2 +// Called with lock held +mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu32 random) +{ + mDNSBool InvokeCallback = mDNStrue; + mDNSIPPort UpdatePort = zeroIPPort; -Revision 1.3 2004/01/23 23:23:15 ksekar -Added TCP support for truncated unicast messages. + mDNS_CheckLock(m); -Revision 1.2 2004/01/22 03:48:41 cheshire -Make sure uDNS client doesn't accidentally use query ID zero + LogInfo("hndlRecordUpdateReply: err %d ID %d state %d %s(%p)", err, mDNSVal16(rr->updateid), rr->state, ARDisplayString(m, rr), rr); -Revision 1.1 2003/12/13 03:05:27 ksekar -<rdar://problem/3192548>: DynDNS: Unicast query of service records + rr->updateError = err; +#if APPLE_OSX_mDNSResponder + if (err == mStatus_BadSig || err == mStatus_BadKey || err == mStatus_BadTime) UpdateAutoTunnelDomainStatuses(m); +#endif - */ + SetRecordRetry(m, rr, random); + + rr->updateid = zeroID; // Make sure that this is not considered as part of a group anymore + // Later when need to send an update, we will get the zone data again. Thus we avoid + // using stale information. + // + // Note: By clearing out the zone info here, it also helps better merging of records + // in some cases. For example, when we get out regState_NoTarget state e.g., move out + // of Double NAT, we want all the records to be in one update. Some BTMM records like + // _autotunnel6 and host records are registered/deregistered when NAT state changes. + // As they are re-registered the zone information is cleared out. To merge with other + // records that might be possibly going out, clearing out the information here helps + // as all of them try to get the zone data. + if (rr->nta) + { + // We always expect the question to be stopped when we get a valid response from the server. + // If the zone info tries to change during this time, updateid would be different and hence + // this response should not have been accepted. + if (rr->nta->question.ThisQInterval != -1) + LogMsg("hndlRecordUpdateReply: ResourceRecord %s, zone info question %##s (%s) interval %d not -1", + ARDisplayString(m, rr), rr->nta->question.qname.c, DNSTypeName(rr->nta->question.qtype), rr->nta->question.ThisQInterval); + UpdatePort = rr->nta->Port; + CancelGetZoneData(m, rr->nta); + rr->nta = mDNSNULL; + } -#pragma ident "%Z%%M% %I% %E% SMI" + // If we are deregistering the record, then complete the deregistration. Ignore any NAT/SRV change + // that could have happened during that time. + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->state == regState_DeregPending) + { + debugf("hndlRecordUpdateReply: Received reply for deregister record %##s type %d", rr->resrec.name->c, rr->resrec.rrtype); + if (err) LogMsg("ERROR: Deregistration of record %##s type %d failed with error %d", + rr->resrec.name->c, rr->resrec.rrtype, err); + rr->state = regState_Unregistered; + CompleteDeregistration(m, rr); + return; + } -#include "uDNS.h" + // We are returning early without updating the state. When we come back from sleep we will re-register after + // re-initializing all the state as though it is a first registration. If the record can't be registered e.g., + // no target, it will be deregistered. Hence, the updating to the right state should not matter when going + // to sleep. + if (m->SleepState) + { + // Need to set it to NoTarget state so that RecordReadyForSleep knows that + // we are done + if (rr->resrec.rrtype == kDNSType_SRV && rr->state == regState_DeregPending) + rr->state = regState_NoTarget; + return; + } -#if(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 + if (rr->state == regState_UpdatePending) + { + if (err) LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err); + rr->state = regState_Registered; + // deallocate old RData + if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen); + SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); + rr->OrigRData = mDNSNULL; + rr->InFlightRData = mDNSNULL; + } -#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 (rr->SRVChanged) + { + if (rr->resrec.rrtype == kDNSType_SRV) + hndlSRVChanged(m, rr); + else + { + LogInfo("hndlRecordUpdateReply: Deregistered %##s (%s), state %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->state); + rr->SRVChanged = mDNSfalse; + if (rr->state != regState_DeregPending) LogMsg("hndlRecordUpdateReply: ResourceRecord %s not in DeregPending state %d", ARDisplayString(m, rr), rr->state); + rr->state = regState_NoTarget; // Wait for the next target change + } + return; + } -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - Temporary workaround -#endif + if (rr->state == regState_Pending || rr->state == regState_Refresh) + { + if (!err) + { + if (rr->state == regState_Refresh) InvokeCallback = mDNSfalse; + rr->state = regState_Registered; + } + else + { + // Retry without lease only for non-Private domains + LogMsg("hndlRecordUpdateReply: Registration of record %##s type %d failed with error %d", rr->resrec.name->c, rr->resrec.rrtype, err); + if (!rr->Private && rr->uselease && err == mStatus_UnknownErr && mDNSSameIPPort(UpdatePort, UnicastDNSPort)) + { + LogMsg("hndlRecordUpdateReply: Will retry update of record %##s without lease option", rr->resrec.name->c); + rr->uselease = mDNSfalse; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + SetNextuDNSEvent(m, rr); + return; + } + // Communicate the error to the application in the callback below + } + } -// 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 + if (rr->QueuedRData && rr->state == regState_Registered) + { + rr->state = regState_UpdatePending; + rr->InFlightRData = rr->QueuedRData; + rr->InFlightRDLen = rr->QueuedRDLen; + rr->OrigRData = rr->resrec.rdata; + rr->OrigRDLen = rr->resrec.rdlength; + rr->QueuedRData = mDNSNULL; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + SetNextuDNSEvent(m, rr); + return; + } -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"); + // Don't invoke the callback on error as this may not be useful to the client. + // The client may potentially delete the resource record on error which we normally + // delete during deregistration + if (!err && InvokeCallback && rr->RecordCallback) + { + LogInfo("hndlRecordUpdateReply: Calling record callback on %##s", rr->resrec.name->c); + mDNS_DropLockBeforeCallback(); + rr->RecordCallback(m, rr, err); + mDNS_ReclaimLockAfterCallback(); + } + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. +} + +mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len) +{ + NATTraversalInfo *ptr; + NATAddrReply *AddrReply = (NATAddrReply *)pkt; + NATPortMapReply *PortMapReply = (NATPortMapReply *)pkt; + mDNSu32 nat_elapsed, our_elapsed; + + // Minimum NAT-PMP packet is vers (1) opcode (1) + err (2) = 4 bytes + if (len < 4) { LogMsg("NAT-PMP message too short (%d bytes)", len); return; } + + // Read multi-byte error value (field is identical in a NATPortMapReply) + AddrReply->err = (mDNSu16) ((mDNSu16)pkt[2] << 8 | pkt[3]); + + if (AddrReply->err == NATErr_Vers) + { + NATTraversalInfo *n; + LogInfo("NAT-PMP version unsupported message received"); + for (n = m->NATTraversals; n; n=n->next) + { + // Send a NAT-PMP request for this operation as needed + // and update the state variables + uDNS_SendNATMsg(m, n, mDNSfalse); + } + + m->NextScheduledNATOp = m->timenow; - // 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)); - } + return; + } -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - General Utility Functions + // The minimum reasonable NAT-PMP packet length is vers (1) + opcode (1) + err (2) + upseconds (4) = 8 bytes + // If it's not at least this long, bail before we byte-swap the upseconds field & overrun our buffer. + // The retry timer will ensure we converge to correctness. + if (len < 8) + { + LogMsg("NAT-PMP message too short (%d bytes) 0x%X 0x%X", len, AddrReply->opcode, AddrReply->err); + return; + } + + // Read multi-byte upseconds value (field is identical in a NATPortMapReply) + AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]); + + nat_elapsed = AddrReply->upseconds - m->LastNATupseconds; + our_elapsed = (m->timenow - m->LastNATReplyLocalTime) / mDNSPlatformOneSecond; + debugf("uDNS_ReceiveNATPMPPacket %X upseconds %u nat_elapsed %d our_elapsed %d", AddrReply->opcode, AddrReply->upseconds, nat_elapsed, our_elapsed); + + // We compute a conservative estimate of how much the NAT gateways's clock should have advanced + // 1. We subtract 12.5% from our own measured elapsed time, to allow for NAT gateways that have an inacurate clock that runs slowly + // 2. We add a two-second safety margin to allow for rounding errors: e.g. + // -- if NAT gateway sends a packet at t=2.000 seconds, then one at t=7.999, that's approximately 6 real seconds, + // but based on the values in the packet (2,7) the apparent difference according to the packet is only 5 seconds + // -- if we're slow handling packets and/or we have coarse clock granularity, + // we could receive the t=2 packet at our t=1.999 seconds, which we round down to 1 + // and the t=7.999 packet at our t=8.000 seconds, which we record as 8, + // giving an apparent local time difference of 7 seconds + // The two-second safety margin coves this possible calculation discrepancy + if (AddrReply->upseconds < m->LastNATupseconds || nat_elapsed + 2 < our_elapsed - our_elapsed/8) + { LogMsg("NAT-PMP epoch time check failed: assuming NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m, 0); } + + m->LastNATupseconds = AddrReply->upseconds; + m->LastNATReplyLocalTime = m->timenow; +#ifdef _LEGACY_NAT_TRAVERSAL_ + LNT_ClearState(m); +#endif // _LEGACY_NAT_TRAVERSAL_ + + if (AddrReply->opcode == NATOp_AddrResponse) + { +#if APPLE_OSX_mDNSResponder + static char msgbuf[16]; + mDNS_snprintf(msgbuf, sizeof(msgbuf), "%d", AddrReply->err); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.AddressRequest", AddrReply->err ? "failure" : "success", msgbuf, ""); +#endif + if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT-PMP AddrResponse message too short (%d bytes)", len); return; } + natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr); + } + else if (AddrReply->opcode == NATOp_MapUDPResponse || AddrReply->opcode == NATOp_MapTCPResponse) + { + mDNSu8 Protocol = AddrReply->opcode & 0x7F; +#if APPLE_OSX_mDNSResponder + static char msgbuf[16]; + mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s - %d", AddrReply->opcode == NATOp_MapUDPResponse ? "UDP" : "TCP", PortMapReply->err); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.PortMapRequest", PortMapReply->err ? "failure" : "success", msgbuf, ""); #endif + if (!PortMapReply->err) + { + if (len < sizeof(NATPortMapReply)) { LogMsg("NAT-PMP PortMapReply message too short (%d bytes)", len); return; } + PortMapReply->NATRep_lease = (mDNSu32) ((mDNSu32)pkt[12] << 24 | (mDNSu32)pkt[13] << 16 | (mDNSu32)pkt[14] << 8 | pkt[15]); + } -// 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; - } + // Since some NAT-PMP server implementations don't return the requested internal port in + // the reply, we can't associate this reply with a particular NATTraversalInfo structure. + // We globally keep track of the most recent error code for mappings. + m->LastNATMapResultCode = PortMapReply->err; -// 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; - } - + for (ptr = m->NATTraversals; ptr; ptr=ptr->next) + if (ptr->Protocol == Protocol && mDNSSameIPPort(ptr->IntPort, PortMapReply->intport)) + natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease, NATTProtocolNATPMP); + } + else { LogMsg("Received NAT-PMP response with unknown opcode 0x%X", AddrReply->opcode); return; } + + // Don't need an SSDP socket if we get a NAT-PMP packet + if (m->SSDPSocket) { debugf("uDNS_ReceiveNATPMPPacket destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } +} + +mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len) +{ + NATTraversalInfo *ptr; + PCPMapReply *reply = (PCPMapReply*)pkt; + mDNSu32 client_delta, server_delta; + mDNSBool checkEpochValidity = m->LastNATupseconds != 0; + mDNSu8 strippedOpCode; + mDNSv4Addr mappedAddress = zerov4Addr; + mDNSu8 protocol = 0; + mDNSIPPort intport = zeroIPPort; + mDNSIPPort extport = zeroIPPort; + + // Minimum PCP packet is 24 bytes + if (len < 24) + { + LogMsg("uDNS_ReceivePCPPacket: message too short (%d bytes)", len); + return; + } + + strippedOpCode = reply->opCode & 0x7f; + + if ((reply->opCode & 0x80) == 0x00 || (strippedOpCode != PCPOp_Announce && strippedOpCode != PCPOp_Map)) + { + LogMsg("uDNS_ReceivePCPPacket: unhandled opCode %u", reply->opCode); + return; + } -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - Name Server List Management -#endif + // Read multi-byte values + reply->lifetime = (mDNSs32)((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[ 6] << 8 | pkt[ 7]); + reply->epoch = (mDNSs32)((mDNSs32)pkt[8] << 24 | (mDNSs32)pkt[9] << 16 | (mDNSs32)pkt[10] << 8 | pkt[11]); -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 + client_delta = (m->timenow - m->LastNATReplyLocalTime) / mDNSPlatformOneSecond; + server_delta = reply->epoch - m->LastNATupseconds; + debugf("uDNS_ReceivePCPPacket: %X %X upseconds %u client_delta %d server_delta %d", reply->opCode, reply->result, reply->epoch, client_delta, server_delta); -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 + // If seconds since the epoch is 0, use 1 so we'll check epoch validity next time + m->LastNATupseconds = reply->epoch ? reply->epoch : 1; + m->LastNATReplyLocalTime = m->timenow; -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; + LNT_ClearState(m); #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); - } + // Don't need an SSDP socket if we get a PCP packet + if (m->SSDPSocket) { debugf("uDNS_ReceivePCPPacket: destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } -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); + if (checkEpochValidity && (client_delta + 2 < server_delta - server_delta / 16 || server_delta + 2 < client_delta - client_delta / 16)) + { + // If this is an ANNOUNCE packet, wait a random interval up to 5 seconds + // otherwise, refresh immediately + mDNSu32 waitTicks = strippedOpCode ? 0 : mDNSRandom(PCP_WAITSECS_AFTER_EPOCH_INVALID * mDNSPlatformOneSecond); + LogMsg("uDNS_ReceivePCPPacket: Epoch invalid, %#a likely rebooted, waiting %u ticks", &m->Router, waitTicks); + RecreateNATMappings(m, waitTicks); + // we can ignore the rest of this packet, as new requests are about to go out + return; + } - rr->RecordContext = (void *)hi->StatusContext; - if (hi->StatusCallback) - hi->StatusCallback(m, rr, result); // client may NOT make API calls here - rr->RecordContext = (void *)hi; - } + if (strippedOpCode == PCPOp_Announce) + return; + + // We globally keep track of the most recent error code for mappings. + // This seems bad to do with PCP, but best not change it now. + m->LastNATMapResultCode = reply->result; + + if (!reply->result) + { + if (len < sizeof(PCPMapReply)) + { + LogMsg("uDNS_ReceivePCPPacket: mapping response too short (%d bytes)", len); + return; + } + + // Check the nonce + if (reply->nonce[0] != m->PCPNonce[0] || reply->nonce[1] != m->PCPNonce[1] || reply->nonce[2] != m->PCPNonce[2]) + { + LogMsg("uDNS_ReceivePCPPacket: invalid nonce, ignoring. received { %x %x %x } expected { %x %x %x }", + reply->nonce[0], reply->nonce[1], reply->nonce[2], + m->PCPNonce[0], m->PCPNonce[1], m->PCPNonce[2]); + return; + } -// 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); - } - } + // Get the values + protocol = reply->protocol; + intport = reply->intPort; + extport = reply->extPort; -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; } + // Get the external address, which should be mapped, since we only support IPv4 + if (!mDNSAddrIPv4FromMappedIPv6(&reply->extAddress, &mappedAddress)) + { + LogMsg("uDNS_ReceivePCPPacket: unexpected external address: %.16a", &reply->extAddress); + reply->result = NATErr_NetFail; + // fall through to report the error + } + else if (mDNSIPv4AddressIsZero(mappedAddress)) + { + // If this is the deletion case, we will have sent the zero IPv4-mapped address + // in our request, and the server should reflect it in the response, so we + // should not log about receiving a zero address. And in this case, we no + // longer have a NATTraversal to report errors back to, so it's ok to set the + // result here. + // In other cases, a zero address is an error, and we will have a NATTraversal + // to report back to, so set an error and fall through to report it. + // CheckNATMappings will log the error. + reply->result = NATErr_NetFail; + } + } + else + { + LogInfo("uDNS_ReceivePCPPacket: error received from server. opcode %X result %X lifetime %X epoch %X", + reply->opCode, reply->result, reply->lifetime, reply->epoch); + + // If the packet is long enough, get the protocol & intport for matching to report + // the error + if (len >= sizeof(PCPMapReply)) + { + protocol = reply->protocol; + intport = reply->intPort; + } + } - 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; + for (ptr = m->NATTraversals; ptr; ptr=ptr->next) + { + mDNSu8 ptrProtocol = ((ptr->Protocol & NATOp_MapTCP) == NATOp_MapTCP ? PCPProto_TCP : PCPProto_UDP); + if ((protocol == ptrProtocol && mDNSSameIPPort(ptr->IntPort, intport)) || + (!ptr->Protocol && protocol == PCPProto_TCP && mDNSSameIPPort(DiscardPort, intport))) + { + natTraversalHandlePortMapReplyWithAddress(m, ptr, InterfaceID, reply->result ? NATErr_NetFail : NATErr_None, mappedAddress, extport, reply->lifetime, NATTProtocolPCP); + } + } +} + +mDNSexport void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len) +{ + if (len == 0) + LogMsg("uDNS_ReceiveNATPacket: zero length packet"); + else if (pkt[0] == PCP_VERS) + uDNS_ReceivePCPPacket(m, InterfaceID, pkt, len); + else if (pkt[0] == NATMAP_VERS) + uDNS_ReceiveNATPMPPacket(m, InterfaceID, pkt, len); + else + LogMsg("uDNS_ReceiveNATPacket: packet with version %u (expected %u or %u)", pkt[0], PCP_VERS, NATMAP_VERS); +} + +// <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs +// <rdar://problem/4288449> Add check to avoid crashing NAT gateways that have buggy DNS relay code +// +// We know of bugs in home NAT gateways that cause them to crash if they receive certain DNS queries. +// The DNS queries that make them crash are perfectly legal DNS queries, but even if they weren't, +// the gateway shouldn't crash -- in today's world of viruses and network attacks, software has to +// be written assuming that a malicious attacker could send them any packet, properly-formed or not. +// Still, we don't want to be crashing people's home gateways, so we go out of our way to avoid +// the queries that crash them. +// +// Some examples: +// +// 1. Any query where the name ends in ".in-addr.arpa." and the text before this is 32 or more bytes. +// The query type does not need to be PTR -- the gateway will crash for any query type. +// e.g. "ping long-name-crashes-the-buggy-router.in-addr.arpa" will crash one of these. +// +// 2. Any query that results in a large response with the TC bit set. +// +// 3. Any PTR query that doesn't begin with four decimal numbers. +// These gateways appear to assume that the only possible PTR query is a reverse-mapping query +// (e.g. "1.0.168.192.in-addr.arpa") and if they ever get a PTR query where the first four +// labels are not all decimal numbers in the range 0-255, they handle that by crashing. +// These gateways also ignore the remainder of the name following the four decimal numbers +// -- whether or not it actually says in-addr.arpa, they just make up an answer anyway. +// +// The challenge therefore is to craft a query that will discern whether the DNS server +// is one of these buggy ones, without crashing it. Furthermore we don't want our test +// queries making it all the way to the root name servers, putting extra load on those +// name servers and giving Apple a bad reputation. To this end we send this query: +// dig -t ptr 1.0.0.127.dnsbugtest.1.0.0.127.in-addr.arpa. +// +// The text preceding the ".in-addr.arpa." is under 32 bytes, so it won't cause crash (1). +// It will not yield a large response with the TC bit set, so it won't cause crash (2). +// It starts with four decimal numbers, so it won't cause crash (3). +// The name falls within the "1.0.0.127.in-addr.arpa." domain, the reverse-mapping name for the local +// loopback address, and therefore the query will black-hole at the first properly-configured DNS server +// it reaches, making it highly unlikely that this query will make it all the way to the root. +// +// Finally, the correct response to this query is NXDOMAIN or a similar error, but the +// gateways that ignore the remainder of the name following the four decimal numbers +// give themselves away by actually returning a result for this nonsense query. + +mDNSlocal const domainname *DNSRelayTestQuestion = (const domainname*) + "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest" + "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\x7" "in-addr" "\x4" "arpa"; + +// See comments above for DNSRelayTestQuestion +// If this is the kind of query that has the risk of crashing buggy DNS servers, we do a test question first +mDNSlocal mDNSBool NoTestQuery(DNSQuestion *q) +{ + int i; + mDNSu8 *p = q->qname.c; + if (q->AuthInfo) return(mDNStrue); // Don't need a test query for private queries sent directly to authoritative server over TLS/TCP + if (q->qtype != kDNSType_PTR) return(mDNStrue); // Don't need a test query for any non-PTR queries + for (i=0; i<4; i++) // If qname does not begin with num.num.num.num, can't skip the test query + { + if (p[0] < 1 || p[0] > 3) return(mDNSfalse); + if ( p[1] < '0' || p[1] > '9' ) return(mDNSfalse); + if (p[0] >= 2 && (p[2] < '0' || p[2] > '9')) return(mDNSfalse); + if (p[0] >= 3 && (p[3] < '0' || p[3] > '9')) return(mDNSfalse); + p += 1 + p[0]; + } + // If remainder of qname is ".in-addr.arpa.", this is a vanilla reverse-mapping query and + // we can safely do it without needing a test query first, otherwise we need the test query. + return(SameDomainName((domainname*)p, (const domainname*)"\x7" "in-addr" "\x4" "arpa")); +} - 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); - } - } +// Returns mDNStrue if response was handled +mDNSlocal mDNSBool uDNS_ReceiveTestQuestionResponse(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport) +{ + const mDNSu8 *ptr = msg->data; + DNSQuestion pktq; + DNSServer *s; + mDNSu32 result = 0; + + // 1. Find out if this is an answer to one of our test questions + if (msg->h.numQuestions != 1) return(mDNSfalse); + ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &pktq); + if (!ptr) return(mDNSfalse); + if (pktq.qtype != kDNSType_PTR || pktq.qclass != kDNSClass_IN) return(mDNSfalse); + if (!SameDomainName(&pktq.qname, DNSRelayTestQuestion)) return(mDNSfalse); + + // 2. If the DNS relay gave us a positive response, then it's got buggy firmware + // else, if the DNS relay gave us an error or no-answer response, it passed our test + if ((msg->h.flags.b[1] & kDNSFlag1_RC_Mask) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0) + result = DNSServer_Failed; + else + result = DNSServer_Passed; + + // 3. Find occurrences of this server in our list, and mark them appropriately + for (s = m->DNSServers; s; s = s->next) + { + mDNSBool matchaddr = (s->teststate != result && mDNSSameAddress(srcaddr, &s->addr) && mDNSSameIPPort(srcport, s->port)); + mDNSBool matchid = (s->teststate == DNSServer_Untested && mDNSSameOpaque16(msg->h.id, s->testid)); + if (matchaddr || matchid) + { + DNSQuestion *q; + s->teststate = result; + if (result == DNSServer_Passed) + { + LogInfo("DNS Server %#a:%d (%#a:%d) %d passed%s", + &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), + matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); + } + else + { + LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a:%d (%#a:%d) %d%s", + &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), + matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); + } -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); - } + // If this server has just changed state from DNSServer_Untested to DNSServer_Passed, then retrigger any waiting questions. + // We use the NoTestQuery() test so that we only retrigger questions that were actually blocked waiting for this test to complete. + if (result == DNSServer_Passed) // Unblock any questions that were waiting for this result + for (q = m->Questions; q; q=q->next) + if (q->qDNSServer == s && !NoTestQuery(q)) + { + q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; + q->unansweredQueries = 0; + q->LastQTime = m->timenow - q->ThisQInterval; + m->NextScheduledQuery = m->timenow; + } + } + } -mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) - { - uDNS_GlobalInfo *u = &m->uDNS_info; - uDNS_HostnameInfo **ptr = &u->Hostnames; + return(mDNStrue); // Return mDNStrue to tell uDNS_ReceiveMsg it doesn't need to process this packet further +} - mDNS_Lock(m); +// Called from mDNSCoreReceive with the lock held +mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport) +{ + DNSQuestion *qptr; + mStatus err = mStatus_NoError; - 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); - } + mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + mDNSu8 rcode = (mDNSu8)(msg->h.flags.b[1] & kDNSFlag1_RC_Mask); -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); - } + (void)srcport; // Unused -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - Incoming Message Processing -#endif + debugf("uDNS_ReceiveMsg from %#-15a with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", + srcaddr, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s", end - msg->data); -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"; + if (QR_OP == StdR) + { + //if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport)) return; + if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return; + for (qptr = m->Questions; qptr; qptr = qptr->next) + if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW) + { + if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring"); + else + { + // Don't reuse TCP connections. We might have failed over to a different DNS server + // while the first TCP connection is in progress. We need a new TCP connection to the + // new DNS server. So, always try to establish a new connection. + if (qptr->tcp) { DisposeTCPConn(qptr->tcp); qptr->tcp = mDNSNULL; } + qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, mDNSNULL, qptr, mDNSNULL); + } + } + } -// 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 (QR_OP == UpdateR) + { + mDNSu32 lease = GetPktLease(m, msg, end); + mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond; + mDNSu32 random = mDNSRandom((mDNSs32)lease * mDNSPlatformOneSecond/10); + + //rcode = kDNSFlag1_RC_ServFail; // Simulate server failure (rcode 2) + + // Walk through all the records that matches the messageID. There could be multiple + // records if we had sent them in a group + if (m->CurrentRecord) + LogMsg("uDNS_ReceiveMsg ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rptr = m->CurrentRecord; + m->CurrentRecord = m->CurrentRecord->next; + if (AuthRecord_uDNS(rptr) && mDNSSameOpaque16(rptr->updateid, msg->h.id)) + { + err = checkUpdateResult(m, rptr->resrec.name, rcode, msg, end); + if (!err && rptr->uselease && lease) + if (rptr->expire - expire >= 0 || rptr->state != regState_UpdatePending) + { + rptr->expire = expire; + rptr->refreshCount = 0; + } + // We pass the random value to make sure that if we update multiple + // records, they all get the same random value + hndlRecordUpdateReply(m, rptr, err, random); + } + } + } + debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id)); +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Query Routines #endif -#define sameID(x,y) mDNSPlatformMemSame(x,y,8) +mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) +{ + mDNSu8 *end; + LLQOptData llq; + mDNSu8 *limit = m->omsg.data + AbsoluteMaxDNSMessageData; + + if (q->ReqLease) + if ((q->state == LLQ_Established && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0) + { + LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d seconds", q->qname.c, DNSTypeName(q->qtype), LLQ_POLL_INTERVAL / mDNSPlatformOneSecond); + StartLLQPolling(m,q); + return; + } + + llq.vers = kLLQ_Vers; + llq.llqOp = kLLQOp_Refresh; + llq.err = q->tcp ? GetLLQEventPort(m, &q->servAddr) : LLQErr_NoError; // If using TCP tell server what UDP port to send notifications to + llq.id = q->id; + llq.llqlease = q->ReqLease; + + InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags); + end = putLLQ(&m->omsg, m->omsg.data, q, &llq); + if (!end) { LogMsg("sendLLQRefresh: putLLQ failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } -mDNSlocal void initializeQuery(DNSMessage *msg, DNSQuestion *question) - { - ubzero(msg, sizeof(msg)); - InitializeDNSMessage(&msg->h, question->uDNS_info.id, uQueryFlags); - } + // Note that we (conditionally) add HINFO and TSIG here, since the question might be going away, + // so we may not be able to reference it (most importantly it's AuthInfo) when we actually send the message + end = putHINFO(m, &m->omsg, end, q->AuthInfo, limit); + if (!end) { LogMsg("sendLLQRefresh: putHINFO failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } -mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question) - { - initializeQuery(msg, question); + if (PrivateQuery(q)) + { + DNSDigest_SignMessageHostByteOrder(&m->omsg, &end, q->AuthInfo); + if (!end) { LogMsg("sendLLQRefresh: DNSDigest_SignMessage failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + } - *endPtr = putQuestion(msg, msg->data, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); - if (!*endPtr) + if (PrivateQuery(q) && !q->tcp) + { + LogInfo("sendLLQRefresh setting up new TLS session %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (!q->nta) { - LogMsg("ERROR: Unicast query out of space in packet"); - return mStatus_UnknownErr; + // Note: If a question is in LLQ_Established state, we never free the zone data for the + // question (PrivateQuery). If we free, we reset the state to something other than LLQ_Established. + // This function is called only if the query is in LLQ_Established state and hence nta should + // never be NULL. In spite of that, we have seen q->nta being NULL in the field. Just refetch the + // zone data in that case. + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); + return; + // ThisQInterval is not adjusted when we return from here which means that we will get called back + // again immediately. As q->servAddr and q->servPort are still valid and the nta->Host is initialized + // without any additional discovery for PrivateQuery, things work. } - 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) + q->tcp = MakeTCPConn(m, &m->omsg, end, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); + } + else + { + mStatus err; + + // if AuthInfo and AuthInfo->AutoTunnel is set, we use the TCP socket but don't need to pass the AuthInfo as + // we already protected the message above. + LogInfo("sendLLQRefresh: using existing %s session %##s (%s)", PrivateQuery(q) ? "TLS" : "UDP", + q->qname.c, DNSTypeName(q->qtype)); + + err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, mDNSNULL, mDNSfalse); + if (err) + { + LogMsg("sendLLQRefresh: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); + if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } + } + } + + q->ntries++; + + debugf("sendLLQRefresh ntries %d %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype)); + + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); +} + +mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) +{ + DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext; + + mDNS_Lock(m); + + // If we get here it means that the GetZoneData operation has completed. + // We hold on to the zone data if it is AutoTunnel as we use the hostname + // in zoneInfo during the TLS connection setup. + q->servAddr = zeroAddr; + q->servPort = zeroIPPort; + + if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0]) + { + q->servAddr = zoneInfo->Addr; + q->servPort = zoneInfo->Port; + if (!PrivateQuery(q)) { - if (qptr == question) + // We don't need the zone data as we use it only for the Host information which we + // don't need if we are not going to use TLS connections. + if (q->nta) { - if (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; + if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; } - 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); + q->ntries = 0; + debugf("LLQGotZoneData %#a:%d", &q->servAddr, mDNSVal16(q->servPort)); + startLLQHandshake(m, q); } + else + { + if (q->nta) + { + if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + } + StartLLQPolling(m,q); + if (err == mStatus_NoSuchNameErr) + { + // this actually failed, so mark it by setting address to all ones + q->servAddr.type = mDNSAddrType_IPv4; + q->servAddr.ip.v4 = onesIPv4Addr; + } + } + + mDNS_Unlock(m); +} + +// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1) +mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) +{ + DNSQuestion *q = (DNSQuestion *) zoneInfo->ZoneDataContext; + LogInfo("PrivateQueryGotZoneData %##s (%s) err %d Zone %##s Private %d", q->qname.c, DNSTypeName(q->qtype), err, zoneInfo->ZoneName.c, zoneInfo->ZonePrivate); + if (q->nta != zoneInfo) LogMsg("PrivateQueryGotZoneData:ERROR!!: nta (%p) != zoneInfo (%p) %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype)); + + if (err || !zoneInfo || mDNSAddressIsZero(&zoneInfo->Addr) || mDNSIPPortIsZero(zoneInfo->Port) || !zoneInfo->Host.c[0]) + { + LogInfo("PrivateQueryGotZoneData: ERROR!! %##s (%s) invoked with error code %d %p %#a:%d", + q->qname.c, DNSTypeName(q->qtype), err, zoneInfo, + zoneInfo ? &zoneInfo->Addr : mDNSNULL, + zoneInfo ? mDNSVal16(zoneInfo->Port) : 0); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + return; + } + + if (!zoneInfo->ZonePrivate) + { + debugf("Private port lookup failed -- retrying without TLS -- %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->AuthInfo = mDNSNULL; // Clear AuthInfo so we try again non-private + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + mDNS_Lock(m); + SetNextQueryTime(m, q); + mDNS_Unlock(m); + return; + // Next call to uDNS_CheckCurrentQuestion() will do this as a non-private query + } + + if (!PrivateQuery(q)) + { + LogMsg("PrivateQueryGotZoneData: ERROR!! Not a private query %##s (%s) AuthInfo %p", q->qname.c, DNSTypeName(q->qtype), q->AuthInfo); + CancelGetZoneData(m, q->nta); + q->nta = mDNSNULL; + return; + } + + q->TargetQID = mDNS_NewMessageID(m); + if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; } + if (!q->nta) { LogMsg("PrivateQueryGotZoneData:ERROR!! nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, &q->nta->Host, q, mDNSNULL); + if (q->nta) { CancelGetZoneData(m, q->nta); q->nta = mDNSNULL; } +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - Domain -> Name Server Conversion +#pragma mark - Dynamic Updates #endif +// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1) +mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData) +{ + AuthRecord *newRR = (AuthRecord*)zoneData->ZoneDataContext; + AuthRecord *ptr; + int c1, c2; -/* 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.) - */ - + if (newRR->nta != zoneData) + LogMsg("RecordRegistrationGotZoneData: nta (%p) != zoneData (%p) %##s (%s)", newRR->nta, zoneData, newRR->resrec.name->c, DNSTypeName(newRR->resrec.rrtype)); -// state machine types and structs -// + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("RecordRegistrationGotZoneData: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); -// 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; + // make sure record is still in list (!!!) + for (ptr = m->ResourceRecords; ptr; ptr = ptr->next) if (ptr == newRR) break; + if (!ptr) + { + LogMsg("RecordRegistrationGotZoneData - RR no longer in list. Discarding."); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; } -// 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; + // check error/result + if (err) + { + if (err != mStatus_NoSuchNameErr) LogMsg("RecordRegistrationGotZoneData: error %d", err); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } - 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) + if (!zoneData) { LogMsg("ERROR: RecordRegistrationGotZoneData invoked with NULL result and no error"); return; } + + if (newRR->resrec.rrclass != zoneData->ZoneClass) { - mStatus err; - LargeCacheRecord lcr; - ResourceRecord *rr = &lcr.r.resrec; - DNSQuestion *query = &context->question; - const mDNSu8 *ptr; - - if (msg) + LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)", newRR->resrec.rrclass, zoneData->ZoneClass); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + // Don't try to do updates to the root name server. + // We might be tempted also to block updates to any single-label name server (e.g. com, edu, net, etc.) but some + // organizations use their own private pseudo-TLD, like ".home", etc, and we don't want to block that. + if (zoneData->ZoneName.c[0] == 0) + { + LogInfo("RecordRegistrationGotZoneData: No name server found claiming responsibility for \"%##s\"!", newRR->resrec.name->c); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + // Store discovered zone data + c1 = CountLabels(newRR->resrec.name); + c2 = CountLabels(&zoneData->ZoneName); + if (c2 > c1) + { + LogMsg("RecordRegistrationGotZoneData: Zone \"%##s\" is longer than \"%##s\"", zoneData->ZoneName.c, newRR->resrec.name->c); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + newRR->zone = SkipLeadingLabels(newRR->resrec.name, c1-c2); + if (!SameDomainName(newRR->zone, &zoneData->ZoneName)) + { + LogMsg("RecordRegistrationGotZoneData: Zone \"%##s\" does not match \"%##s\" for \"%##s\"", newRR->zone->c, zoneData->ZoneName.c, newRR->resrec.name->c); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + if (mDNSIPPortIsZero(zoneData->Port) || mDNSAddressIsZero(&zoneData->Addr) || !zoneData->Host.c[0]) + { + LogInfo("RecordRegistrationGotZoneData: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c); + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + + newRR->Private = zoneData->ZonePrivate; + debugf("RecordRegistrationGotZoneData: Set zone information for %##s %##s to %#a:%d", + newRR->resrec.name->c, zoneData->ZoneName.c, &zoneData->Addr, mDNSVal16(zoneData->Port)); + + // If we are deregistering, uDNS_DeregisterRecord will do that as it has the zone data now. + if (newRR->state == regState_DeregPending) + { + mDNS_Lock(m); + uDNS_DeregisterRecord(m, newRR); + mDNS_Unlock(m); + return; + } + + if (newRR->resrec.rrtype == kDNSType_SRV) + { + const domainname *target; + // Reevaluate the target always as NAT/Target could have changed while + // we were fetching zone data. + mDNS_Lock(m); + target = GetServiceTarget(m, newRR); + mDNS_Unlock(m); + if (!target || target->c[0] == 0) { - // 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]) + domainname *t = GetRRDomainNameTarget(&newRR->resrec); + LogInfo("RecordRegistrationGotZoneData - no target for %##s", newRR->resrec.name->c); + if (t) t->c[0] = 0; + newRR->resrec.rdlength = newRR->resrec.rdestimate = 0; + newRR->state = regState_NoTarget; + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; + } + } + // If we have non-zero service port (always?) + // and a private address, and update server is non-private + // and this service is AutoTarget + // then initiate a NAT mapping request. On completion it will do SendRecordRegistration() for us + if (newRR->resrec.rrtype == kDNSType_SRV && !mDNSIPPortIsZero(newRR->resrec.rdata->u.srv.port) && + mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && newRR->nta && !mDNSAddrIsRFC1918(&newRR->nta->Addr) && + newRR->AutoTarget == Target_AutoHostAndNATMAP) + { + DomainAuthInfo *AuthInfo; + AuthInfo = GetAuthInfoForName(m, newRR->resrec.name); + if (AuthInfo && AuthInfo->AutoTunnel) { - // 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; + domainname *t = GetRRDomainNameTarget(&newRR->resrec); + LogMsg("RecordRegistrationGotZoneData: ERROR!! AutoTunnel has Target_AutoHostAndNATMAP for %s", ARDisplayString(m, newRR)); + if (t) t->c[0] = 0; + newRR->resrec.rdlength = newRR->resrec.rdestimate = 0; + newRR->state = regState_NoTarget; + CancelGetZoneData(m, newRR->nta); + newRR->nta = mDNSNULL; + return; } + // During network transitions, we are called multiple times in different states. Setup NAT + // state just once for this record. + if (!newRR->NATinfo.clientContext) + { + LogInfo("RecordRegistrationGotZoneData StartRecordNatMap %s", ARDisplayString(m, newRR)); + newRR->state = regState_NATMap; + StartRecordNatMap(m, newRR); + return; + } + else LogInfo("RecordRegistrationGotZoneData: StartRecordNatMap for %s, state %d, context %p", ARDisplayString(m, newRR), newRR->state, newRR->NATinfo.clientContext); + } + mDNS_Lock(m); + // We want IsRecordMergeable to check whether it is a record whose update can be + // sent with others. We set the time before we call IsRecordMergeable, so that + // it does not fail this record based on time. We are interested in other checks + // at this time. If a previous update resulted in error, then don't reset the + // interval. Preserve the back-off so that we don't keep retrying aggressively. + if (newRR->updateError == mStatus_NoError) + { + newRR->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + newRR->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + } + if (IsRecordMergeable(m, newRR, m->timenow + MERGE_DELAY_TIME)) + { + // Delay the record registration by MERGE_DELAY_TIME so that we can merge them + // into one update + LogInfo("RecordRegistrationGotZoneData: Delayed registration for %s", ARDisplayString(m, newRR)); + newRR->LastAPTime += MERGE_DELAY_TIME; + } + mDNS_Unlock(m); +} - 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; - } - +mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) +{ + mDNSu8 *ptr = m->omsg.data; + mDNSu8 *limit; + DomainAuthInfo *AuthInfo; -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - Truncation Handling -#endif + mDNS_CheckLock(m); -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 (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) + { + LogMsg("SendRecordDeRegistration: No zone info for Resource record %s RecordType %d", ARDisplayString(m, rr), rr->resrec.RecordType); + return; + } + limit = ptr + AbsoluteMaxDNSMessageData; + AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + limit -= RRAdditionalSize(m, AuthInfo); -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - Dynamic Updates -#endif + rr->updateid = mDNS_NewMessageID(m); + InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags); -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, ®Info->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; - } + ptr = putZone(&m->omsg, ptr, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto exit; - 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, ®Info->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; - } + ptr = BuildUpdateMessage(m, ptr, rr, limit); + if (!ptr) goto exit; + if (rr->Private) + { + LogInfo("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr)); + if (rr->tcp) LogInfo("SendRecordDeregistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr)); + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (!rr->nta) { LogMsg("SendRecordDeregistration:Private:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } + rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->nta->Addr, rr->nta->Port, &rr->nta->Host, mDNSNULL, rr); + } + else + { + mStatus err; + LogInfo("SendRecordDeregistration UDP %s", ARDisplayString(m, rr)); + if (!rr->nta) { LogMsg("SendRecordDeregistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } + err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); + if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %d", err); + //if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this + } + SetRecordRetry(m, rr, 0); + return; +exit: + LogMsg("SendRecordDeregistration: Error formatting message for %s", ARDisplayString(m, rr)); +} mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) - { - 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; - } - +{ + DomainAuthInfo *info; + + LogInfo("uDNS_DeregisterRecord: Resource Record %s, state %d", ARDisplayString(m, rr), rr->state); + + switch (rr->state) + { + case regState_Refresh: + case regState_Pending: + case regState_UpdatePending: + case regState_Registered: break; + case regState_DeregPending: break; + + case regState_NATError: + case regState_NATMap: + // A record could be in NoTarget to start with if the corresponding SRV record could not find a target. + // It is also possible to reenter the NoTarget state when we move to a network with a NAT that has + // no {PCP, NAT-PMP, UPnP/IGD} support. In that case before we entered NoTarget, we already deregistered with + // the server. + case regState_NoTarget: + case regState_Unregistered: + case regState_Zero: + default: + LogInfo("uDNS_DeregisterRecord: State %d for %##s type %s", rr->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + // This function may be called during sleep when there are no sleep proxy servers + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) CompleteDeregistration(m, rr); + return mStatus_NoError; + } + + // if unsent rdata is queued, free it. + // + // The data may be queued in QueuedRData or InFlightRData. + // + // 1) If the record is in Registered state, we store it in InFlightRData and copy the same in "rdata" + // *just* before sending the update to the server. Till we get the response, InFlightRData and "rdata" + // in the resource record are same. We don't want to free in that case. It will be freed when "rdata" + // is freed. If they are not same, the update has not been sent and we should free it here. + // + // 2) If the record is in UpdatePending state, we queue the update in QueuedRData. When the previous update + // comes back from the server, we copy it from QueuedRData to InFlightRData and repeat (1). This implies + // that QueuedRData can never be same as "rdata" in the resource record. As long as we have something + // left in QueuedRData, we should free it here. + + if (rr->InFlightRData && rr->UpdateCallback) + { + if (rr->InFlightRData != rr->resrec.rdata) + { + LogInfo("uDNS_DeregisterRecord: Freeing InFlightRData for %s", ARDisplayString(m, rr)); + rr->UpdateCallback(m, rr, rr->InFlightRData, rr->InFlightRDLen); + rr->InFlightRData = mDNSNULL; + } + else + LogInfo("uDNS_DeregisterRecord: InFlightRData same as rdata for %s", ARDisplayString(m, rr)); + } + + if (rr->QueuedRData && rr->UpdateCallback) + { + if (rr->QueuedRData == rr->resrec.rdata) + LogMsg("uDNS_DeregisterRecord: ERROR!! QueuedRData same as rdata for %s", ARDisplayString(m, rr)); + else + { + LogInfo("uDNS_DeregisterRecord: Freeing QueuedRData for %s", ARDisplayString(m, rr)); + rr->UpdateCallback(m, rr, rr->QueuedRData, rr->QueuedRDLen); + rr->QueuedRData = mDNSNULL; + } + } + + // If a current group registration is pending, we can't send this deregisration till that registration + // has reached the server i.e., the ordering is important. Previously, if we did not send this + // registration in a group, then the previous connection will be torn down as part of sending the + // deregistration. If we send this in a group, we need to locate the resource record that was used + // to send this registration and terminate that connection. This means all the updates on that might + // be lost (assuming the response is not waiting for us at the socket) and the retry will send the + // update again sometime in the near future. + // + // NOTE: SSL handshake failures normally free the TCP connection immediately. Hence, you may not + // find the TCP below there. This case can happen only when tcp is trying to actively retransmit + // the request or SSL negotiation taking time i.e resource record is actively trying to get the + // message to the server. During that time a deregister has to happen. + + if (!mDNSOpaque16IsZero(rr->updateid)) + { + AuthRecord *anchorRR; + mDNSBool found = mDNSfalse; + for (anchorRR = m->ResourceRecords; anchorRR; anchorRR = anchorRR->next) + { + if (AuthRecord_uDNS(rr) && mDNSSameOpaque16(anchorRR->updateid, rr->updateid) && anchorRR->tcp) + { + LogInfo("uDNS_DeregisterRecord: Found Anchor RR %s terminated", ARDisplayString(m, anchorRR)); + if (found) + LogMsg("uDNS_DeregisterRecord: ERROR: Another anchorRR %s found", ARDisplayString(m, anchorRR)); + DisposeTCPConn(anchorRR->tcp); + anchorRR->tcp = mDNSNULL; + found = mDNStrue; + } + } + if (!found) LogInfo("uDNSDeregisterRecord: Cannot find the anchor Resource Record for %s, not an error", ARDisplayString(m, rr)); + } + + // Retry logic for deregistration should be no different from sending registration the first time. + // Currently ThisAPInterval most likely is set to the refresh interval + rr->state = regState_DeregPending; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + info = GetAuthInfoForName_internal(m, rr->resrec.name); + if (IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) + { + // Delay the record deregistration by MERGE_DELAY_TIME so that we can merge them + // into one update. If the domain is being deleted, delay by 2 * MERGE_DELAY_TIME + // so that we can merge all the AutoTunnel records and the service records in + // one update (they get deregistered a little apart) + if (info && info->deltime) rr->LastAPTime += (2 * MERGE_DELAY_TIME); + else rr->LastAPTime += MERGE_DELAY_TIME; + } + // IsRecordMergeable could have returned false for several reasons e.g., DontMerge is set or + // no zone information. Most likely it is the latter, CheckRecordUpdates will fetch the zone + // data when it encounters this record. + + if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval); + + return mStatus_NoError; +} + mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr) - { - 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; - } +{ + LogInfo("uDNS_UpdateRecord: Resource Record %##s, state %d", rr->resrec.name->c, rr->state); + switch(rr->state) + { + case regState_DeregPending: + case regState_Unregistered: + // not actively registered + goto unreg_error; + + case regState_NATMap: + case regState_NoTarget: + // change rdata directly since it hasn't been sent yet + if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->resrec.rdata, rr->resrec.rdlength); + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); + rr->NewRData = mDNSNULL; + return mStatus_NoError; + + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // registration in-flight. queue rdata and return + if (rr->QueuedRData && rr->UpdateCallback) + // if unsent rdata is already queued, free it before we replace it + rr->UpdateCallback(m, rr, rr->QueuedRData, rr->QueuedRDLen); + rr->QueuedRData = rr->NewRData; + rr->QueuedRDLen = rr->newrdlength; + rr->NewRData = mDNSNULL; + return mStatus_NoError; + + case regState_Registered: + rr->OrigRData = rr->resrec.rdata; + rr->OrigRDLen = rr->resrec.rdlength; + rr->InFlightRData = rr->NewRData; + rr->InFlightRDLen = rr->newrdlength; + rr->NewRData = mDNSNULL; + rr->state = regState_UpdatePending; + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; + rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; + SetNextuDNSEvent(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 + + default: LogMsg("uDNS_UpdateRecord: Unknown state %d for %##s", rr->state, rr->resrec.name->c); + } +unreg_error: + LogMsg("uDNS_UpdateRecord: Requested update of record %##s type %d, in erroneous state %d", + rr->resrec.name->c, rr->resrec.rrtype, rr->state); + return mStatus_Invalid; +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Periodic Execution Routines #endif +mDNSlocal void handle_unanswered_query(mDNS *const m) +{ + DNSQuestion *q = m->CurrentQuestion; -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 (q->unansweredQueries >= MAX_DNSSEC_UNANSWERED_QUERIES && DNSSECOptionalQuestion(q)) + { + // If we are not receiving any responses for DNSSEC question, it could be due to + // a broken middlebox or a DNS server that does not understand the EDNS0/DOK option that + // silently drops the packets. Also as per RFC 5625 there are certain buggy DNS Proxies + // that are known to drop these pkts. To handle this, we turn off sending the EDNS0/DOK + // option if we have not received any responses indicating that the server or + // the middlebox is DNSSEC aware. If we receive at least one response to a DNSSEC + // question, we don't turn off validation. Also, we wait for MAX_DNSSEC_RETRANSMISSIONS + // before turning off validation to accomodate packet loss. + // + // Note: req_DO affects only DNSSEC_VALIDATION_SECURE_OPTIONAL questions; + // DNSSEC_VALIDATION_SECURE questions ignores req_DO. + + if (q->qDNSServer && !q->qDNSServer->DNSSECAware && q->qDNSServer->req_DO) + { + q->qDNSServer->retransDO++; + if (q->qDNSServer->retransDO == MAX_DNSSEC_RETRANSMISSIONS) + { + LogInfo("handle_unanswered_query: setting req_DO false for %#a", &q->qDNSServer->addr); + q->qDNSServer->req_DO = mDNSfalse; + } + } + + if (!q->qDNSServer->req_DO) + { + q->ValidationState = DNSSECValNotRequired; + q->ValidationRequired = DNSSEC_VALIDATION_NONE; + + if (q->ProxyQuestion) + q->ProxyDNSSECOK = mDNSfalse; + LogInfo("handle_unanswered_query: unanswered query for %##s (%s), so turned off validation for %#a", + q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr); + } + } +} + +// The question to be checked is not passed in as an explicit parameter; +// instead it is implicit that the question to be checked is m->CurrentQuestion. +mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) +{ + DNSQuestion *q = m->CurrentQuestion; + if (m->timenow - NextQSendTime(q) < 0) return; + + if (q->LongLived) + { + switch (q->state) + { + case LLQ_InitialRequest: startLLQHandshake(m, q); break; + case LLQ_SecondaryRequest: + // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step + if (PrivateQuery(q)) + startLLQHandshake(m, q); + else + sendChallengeResponse(m, q, mDNSNULL); + break; + case LLQ_Established: sendLLQRefresh(m, q); break; + case LLQ_Poll: break; // Do nothing (handled below) + } + } + + handle_unanswered_query(m); + // We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll + if (!(q->LongLived && q->state != LLQ_Poll)) + { + if (q->unansweredQueries >= MAX_UCAST_UNANSWERED_QUERIES) + { + DNSServer *orig = q->qDNSServer; + if (orig) + LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)", + q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c); + + PenalizeDNSServer(m, q, zeroID); + q->noServerResponse = 1; + } + // There are two cases here. + // + // 1. We have only one DNS server for this question. It is not responding even after we sent MAX_UCAST_UNANSWERED_QUERIES. + // In that case, we need to keep retrying till we get a response. But we need to backoff as we retry. We set + // noServerResponse in the block above and below we do not touch the question interval. When we come here, we + // already waited for the response. We need to send another query right at this moment. We do that below by + // reinitializing dns servers and reissuing the query. + // + // 2. We have more than one DNS server. If at least one server did not respond, we would have set noServerResponse + // either now (the last server in the list) or before (non-last server in the list). In either case, if we have + // reached the end of DNS server list, we need to try again from the beginning. Ideally we should try just the + // servers that did not respond, but for simplicity we try all the servers. Once we reached the end of list, we + // set triedAllServersOnce so that we don't try all the servers aggressively. See PenalizeDNSServer. + if (!q->qDNSServer && q->noServerResponse) + { + DNSServer *new; + DNSQuestion *qptr; + q->triedAllServersOnce = 1; + // Re-initialize all DNS servers for this question. If we have a DNSServer, DNSServerChangeForQuestion will + // handle all the work including setting the new DNS server. + SetValidDNSServers(m, q); + new = GetServerForQuestion(m, q); + if (new) + { + mDNSIPPort zp = zeroIPPort; + LogInfo("uDNS_checkCurrentQuestion: Retrying question %p %##s (%s) DNS Server %#a:%d ThisQInterval %d", + q, q->qname.c, DNSTypeName(q->qtype), new ? &new->addr : mDNSNULL, mDNSVal16(new ? new->port : zp), q->ThisQInterval); + DNSServerChangeForQuestion(m, q, new); + } + for (qptr = q->next ; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } + } + if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled) + { + mDNSu8 *end = m->omsg.data; + mStatus err = mStatus_NoError; + mDNSBool private = mDNSfalse; + + InitializeDNSMessage(&m->omsg.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); + + if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q)) + { + end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf) + { + if (q->ProxyQuestion) + end = DNSProxySetAttributes(q, &m->omsg.h, &m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); + else + end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); + } + private = PrivateQuery(q); + } + else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL) // Make sure at least three seconds has elapsed since last test query + { + LogInfo("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port)); + q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; + q->qDNSServer->lasttest = m->timenow; + end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN); + q->qDNSServer->testid = m->omsg.h.id; + } + + if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q))) + { + //LogMsg("uDNS_CheckCurrentQuestion %p %d %p %##s (%s)", q, NextQSendTime(q) - m->timenow, private, q->qname.c, DNSTypeName(q->qtype)); + if (private) + { + if (q->nta) CancelGetZoneData(m, q->nta); + q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, PrivateQueryGotZoneData, q); + if (q->state == LLQ_Poll) q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep; + } + else + { + debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d", + q, q->qname.c, DNSTypeName(q->qtype), + q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries); + if (!q->LocalSocket) + { + q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (q->LocalSocket) + mDNSPlatformSetuDNSSocktOpt(q->LocalSocket, &q->qDNSServer->addr, q); + } + if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time + else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass); + } + } + + if (err != mStatus_TransientErr) // if it is not a transient error backoff and DO NOT flood queries unnecessarily + { + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; // Only increase interval if send succeeded + q->unansweredQueries++; + if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) + q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + if (private && q->state != LLQ_Poll) + { + // We don't want to retransmit too soon. Hence, we always schedule our first + // retransmisson at 3 seconds rather than one second + if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; + if (q->ThisQInterval > LLQ_POLL_INTERVAL) + q->ThisQInterval = LLQ_POLL_INTERVAL; + LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + if (q->qDNSServer->cellIntf) + { + // We don't want to retransmit too soon. Schedule our first retransmisson at + // MIN_UCAST_RETRANS_TIMEOUT seconds. + if (q->ThisQInterval < MIN_UCAST_RETRANS_TIMEOUT) + q->ThisQInterval = MIN_UCAST_RETRANS_TIMEOUT; + } + debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->cellIntf); + } + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + } + else + { + // If we have no server for this query, or the only server is a disabled one, then we deliver + // a transient failure indication to the client. This is important for things like iPhone + // where we want to return timely feedback to the user when no network is available. + // After calling MakeNegativeCacheRecord() we store the resulting record in the + // cache so that it will be visible to other clients asking the same question. + // (When we have a group of identical questions, only the active representative of the group gets + // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire -- + // but we want *all* of the questions to get answer callbacks.) + CacheRecord *rr; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + + if (!q->qDNSServer) + { + if (!mDNSOpaque64IsZero(&q->validDNSServers)) + LogMsg("uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x for question %##s (%s)", + q->validDNSServers.l[1], q->validDNSServers.l[0], q->qname.c, DNSTypeName(q->qtype)); + // If we reached the end of list while picking DNS servers, then we don't want to deactivate the + // question. Try after 60 seconds. We find this by looking for valid DNSServers for this question, + // if we find any, then we must have tried them before we came here. This avoids maintaining + // another state variable to see if we had valid DNS servers for this question. + SetValidDNSServers(m, q); + if (mDNSOpaque64IsZero(&q->validDNSServers)) + { + LogInfo("uDNS_CheckCurrentQuestion: no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = 0; + } + else + { + DNSQuestion *qptr; + // Pretend that we sent this question. As this is an ActiveQuestion, the NextScheduledQuery should + // be set properly. Also, we need to properly backoff in cases where we don't set the question to + // MaxQuestionInterval when we answer the question e.g., LongLived, we need to keep backing off + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + // Pick a new DNS server now. Otherwise, when the cache is 80% of its expiry, we will try + // to send a query and come back to the same place here and log the above message. + q->qDNSServer = GetServerForQuestion(m, q); + for (qptr = q->next ; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } { - 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; + mDNSIPPort zp = zeroIPPort; + LogInfo("uDNS_checkCurrentQuestion: Tried all DNS servers, retry question %p SuppressUnusable %d %##s (%s) with DNS Server %#a:%d after 60 seconds, ThisQInterval %d", + q, q->SuppressUnusable, q->qname.c, DNSTypeName(q->qtype), + q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zp), q->ThisQInterval); } - } - return nextevent; - } + } + } + else + { + q->ThisQInterval = 0; + LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c); + } -mDNSexport void uDNS_Execute(mDNS *const m) - { - uDNS_GlobalInfo *u = &m->uDNS_info; - mDNSs32 nexte, timenow = mDNSPlatformTimeNow(m); + if (cg) + { + for (rr = cg->members; rr; rr=rr->next) + { + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr)); + mDNS_PurgeCacheResourceRecord(m, rr); + } + } + } + // For some of the WAB queries that we generate form within the mDNSResponder, most of the home routers + // don't understand and return ServFail/NXDomain. In those cases, we don't want to try too often. We try + // every fifteen minutes in that case + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, (DomainEnumQuery(&q->qname) ? 60 * 15 : 60), mDNSInterface_Any, q->qDNSServer); + q->unansweredQueries = 0; + if (!mDNSOpaque16IsZero(q->responseFlags)) + m->rec.r.responseFlags = q->responseFlags; + // We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list. + // To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we + // momentarily defer generating answer callbacks until mDNS_Execute time. + CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow), mDNStrue, mDNSNULL); + ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow)); + m->rec.r.responseFlags = zeroID; + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + // MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it + } + } +} - u->nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; +mDNSexport void CheckNATMappings(mDNS *m) +{ + mDNSBool rfc1918 = mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4); + mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4); + m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF; - 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 (HaveRoutable) m->ExtAddress = m->AdvertisedV4.ip.v4; - if (m->SuppressStdPort53Queries && m->timenow - m->SuppressStdPort53Queries >= 0) - m->SuppressStdPort53Queries = 0; // If suppression time has passed, clear it + if (m->NATTraversals && rfc1918) // Do we need to open a socket to receive multicast announcements from router? + { + if (m->NATMcastRecvskt == mDNSNULL) // If we are behind a NAT and the socket hasn't been opened yet, open it + { + // we need to log a message if we can't get our socket, but only the first time (after success) + static mDNSBool needLog = mDNStrue; + m->NATMcastRecvskt = mDNSPlatformUDPSocket(m, NATPMPAnnouncementPort); + if (!m->NATMcastRecvskt) + { + if (needLog) + { + LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for PCP & NAT-PMP announcements"); + needLog = mDNSfalse; + } + } + else + needLog = mDNStrue; + } + } + else // else, we don't want to listen for announcements, so close them if they're open + { + if (m->NATMcastRecvskt) { mDNSPlatformUDPClose(m->NATMcastRecvskt); m->NATMcastRecvskt = mDNSNULL; } + if (m->SSDPSocket) { debugf("CheckNATMappings destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + } + + uDNS_RequestAddress(m); + + if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use"); + m->CurrentNATTraversal = m->NATTraversals; + + while (m->CurrentNATTraversal) + { + NATTraversalInfo *cur = m->CurrentNATTraversal; + mDNSv4Addr EffectiveAddress = HaveRoutable ? m->AdvertisedV4.ip.v4 : cur->NewAddress; + m->CurrentNATTraversal = m->CurrentNATTraversal->next; + + if (HaveRoutable) // If not RFC 1918 address, our own address and port are effectively our external address and port + { + cur->ExpiryTime = 0; + cur->NewResult = mStatus_NoError; + } + else // Check if it's time to send port mapping packet(s) + { + if (m->timenow - cur->retryPortMap >= 0) // Time to send a mapping request for this packet + { + if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0) // Mapping has expired + { + cur->ExpiryTime = 0; + cur->retryInterval = NATMAP_INIT_RETRY; + } + + (void)uDNS_SendNATMsg(m, cur, mDNStrue); // Will also do UPnP discovery for us, if necessary + + if (cur->ExpiryTime) // If have active mapping then set next renewal time halfway to expiry + NATSetNextRenewalTime(m, cur); + else // else no mapping; use exponential backoff sequence + { + if (cur->retryInterval < NATMAP_INIT_RETRY ) cur->retryInterval = NATMAP_INIT_RETRY; + else if (cur->retryInterval < NATMAP_MAX_RETRY_INTERVAL / 2) cur->retryInterval *= 2; + else cur->retryInterval = NATMAP_MAX_RETRY_INTERVAL; + cur->retryPortMap = m->timenow + cur->retryInterval; + } + } + + if (m->NextScheduledNATOp - cur->retryPortMap > 0) + { + m->NextScheduledNATOp = cur->retryPortMap; + } + } + + // Notify the client if necessary. We invoke the callback if: + // (1) We have an effective address, + // or we've tried and failed a couple of times to discover it + // AND + // (2) the client requested the address only, + // or the client won't need a mapping because we have a routable address, + // or the client has an expiry time and therefore a successful mapping, + // or we've tried and failed a couple of times (see "Time line" below) + // AND + // (3) we have new data to give the client that's changed since the last callback + // + // Time line is: Send, Wait 500ms, Send, Wait 1sec, Send, Wait 2sec, Send + // At this point we've sent three requests without an answer, we've just sent our fourth request, + // retryInterval is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds), + // so we return an error result to the caller. + if (!mDNSIPv4AddressIsZero(EffectiveAddress) || cur->retryInterval > NATMAP_INIT_RETRY * 8) + { + const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&EffectiveAddress) ? mStatus_DoubleNAT : mStatus_NoError; + mDNSIPPort ExternalPort; - nexte = CheckQueries(m, timenow); - if (nexte - u->nextevent < 0) u->nextevent = nexte; + if (HaveRoutable) + ExternalPort = cur->IntPort; + else if (!mDNSIPv4AddressIsZero(EffectiveAddress) && cur->ExpiryTime) + ExternalPort = cur->RequestedPort; + else + ExternalPort = zeroIPPort; - nexte = CheckRecordRegistrations(m, timenow); - if (nexte - u->nextevent < 0) u->nextevent = nexte; + if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8) + { + if (!mDNSSameIPv4Address(cur->ExternalAddress, EffectiveAddress) || + !mDNSSameIPPort (cur->ExternalPort, ExternalPort) || + cur->Result != EffectiveResult) + { + //LogMsg("NAT callback %d %d %d", cur->Protocol, cur->ExpiryTime, cur->retryInterval); + if (cur->Protocol && mDNSIPPortIsZero(ExternalPort) && !mDNSIPv4AddressIsZero(m->Router.ip.v4)) + { + if (!EffectiveResult) + LogInfo("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d", + cur, &m->Router, &EffectiveAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); + else + LogMsg("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d", + cur, &m->Router, &EffectiveAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); + } + + cur->ExternalAddress = EffectiveAddress; + cur->ExternalPort = ExternalPort; + cur->Lifetime = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ? + (cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0; + cur->Result = EffectiveResult; + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + if (cur->clientCallback) + cur->clientCallback(m, cur); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + // MUST NOT touch cur after invoking the callback + } + } + } + } +} + +mDNSlocal mDNSs32 CheckRecordUpdates(mDNS *m) +{ + AuthRecord *rr; + mDNSs32 nextevent = m->timenow + 0x3FFFFFFF; - nexte = CheckServiceRegistrations(m, timenow); - if (nexte - u->nextevent < 0) u->nextevent = nexte; - - } + CheckGroupRecordUpdates(m); + + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if (!AuthRecord_uDNS(rr)) continue; + if (rr->state == regState_NoTarget) {debugf("CheckRecordUpdates: Record %##s in NoTarget", rr->resrec.name->c); continue;} + // While we are waiting for the port mapping, we have nothing to do. The port mapping callback + // will take care of this + if (rr->state == regState_NATMap) {debugf("CheckRecordUpdates: Record %##s in NATMap", rr->resrec.name->c); continue;} + if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending || + rr->state == regState_Refresh || rr->state == regState_Registered) + { + if (rr->LastAPTime + rr->ThisAPInterval - m->timenow <= 0) + { + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) + { + // Zero out the updateid so that if we have a pending response from the server, it won't + // be accepted as a valid response. If we accept the response, we might free the new "nta" + if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); } + rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr); + + // We have just started the GetZoneData. We need to wait for it to finish. SetRecordRetry here + // schedules the update timer to fire in the future. + // + // There are three cases. + // + // 1) When the updates are sent the first time, the first retry is intended to be at three seconds + // in the future. But by calling SetRecordRetry here we set it to nine seconds. But it does not + // matter because when the answer comes back, RecordRegistrationGotZoneData resets the interval + // back to INIT_RECORD_REG_INTERVAL. This also gives enough time for the query. + // + // 2) In the case of update errors (updateError), this causes further backoff as + // RecordRegistrationGotZoneData does not reset the timer. This is intentional as in the case of + // errors, we don't want to update aggressively. + // + // 3) We might be refreshing the update. This is very similar to case (1). RecordRegistrationGotZoneData + // resets it back to INIT_RECORD_REG_INTERVAL. + // + SetRecordRetry(m, rr, 0); + } + else if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr); + else SendRecordRegistration(m, rr); + } + } + if (nextevent - (rr->LastAPTime + rr->ThisAPInterval) > 0) + nextevent = (rr->LastAPTime + rr->ThisAPInterval); + } + return nextevent; +} + +mDNSexport void uDNS_Tasks(mDNS *const m) +{ + mDNSs32 nexte; + DNSServer *d; + + m->NextuDNSEvent = m->timenow + 0x3FFFFFFF; + + nexte = CheckRecordUpdates(m); + if (m->NextuDNSEvent - nexte > 0) + m->NextuDNSEvent = nexte; + + for (d = m->DNSServers; d; d=d->next) + if (d->penaltyTime) + { + if (m->timenow - d->penaltyTime >= 0) + { + LogInfo("DNS server %#a:%d out of penalty box", &d->addr, mDNSVal16(d->port)); + d->penaltyTime = 0; + } + else + if (m->NextuDNSEvent - d->penaltyTime > 0) + m->NextuDNSEvent = d->penaltyTime; + } + + if (m->CurrentQuestion) + LogMsg("uDNS_Tasks ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *const q = m->CurrentQuestion; + if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID)) + { + uDNS_CheckCurrentQuestion(m); + if (q == m->CurrentQuestion) + if (m->NextuDNSEvent - NextQSendTime(q) > 0) + m->NextuDNSEvent = NextQSendTime(q); + } + // If m->CurrentQuestion wasn't modified out from under us, advance it now + // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() + // depends on having m->CurrentQuestion point to the right question + if (m->CurrentQuestion == q) + m->CurrentQuestion = q->next; + } + m->CurrentQuestion = mDNSNULL; +} // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Startup, Shutdown, and Sleep #endif -// 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); - } +mDNSexport void SleepRecordRegistrations(mDNS *m) +{ + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (AuthRecord_uDNS(rr)) + { + // Zero out the updateid so that if we have a pending response from the server, it won't + // be accepted as a valid response. + if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } + + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + // We are waiting to update the resource record. The original data of the record is + // in OrigRData and the updated value is in InFlightRData. Free the old and the new + // one will be registered when we come back. + if (rr->state == regState_UpdatePending) + { + // act as if the update succeeded, since we're about to delete the name anyway + rr->state = regState_Registered; + // deallocate old RData + if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen); + SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen); + rr->OrigRData = mDNSNULL; + rr->InFlightRData = mDNSNULL; + } + + // If we have not begun the registration process i.e., never sent a registration packet, + // then uDNS_DeregisterRecord will not send a deregistration + uDNS_DeregisterRecord(m, rr); + + // When we wake, we call ActivateUnicastRegistration which starts at StartGetZoneData + } + } +} + +mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) +{ + SearchListElem **p; + SearchListElem *tmp = mDNSNULL; + + // Check to see if we already have this domain in our list + for (p = &SearchList; *p; p = &(*p)->next) + if (((*p)->InterfaceID == InterfaceID) && SameDomainName(&(*p)->domain, domain)) + { + // If domain is already in list, and marked for deletion, unmark the delete + // Be careful not to touch the other flags that may be present + LogInfo("mDNS_AddSearchDomain already in list %##s", domain->c); + if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE; + tmp = *p; + *p = tmp->next; + tmp->next = mDNSNULL; + break; + } + + + // move to end of list so that we maintain the same order + while (*p) p = &(*p)->next; + + if (tmp) *p = tmp; + else + { + // if domain not in list, add to list, mark as add (1) + *p = mDNSPlatformMemAllocate(sizeof(SearchListElem)); + if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; } + mDNSPlatformMemZero(*p, sizeof(SearchListElem)); + AssignDomainName(&(*p)->domain, domain); + (*p)->next = mDNSNULL; + (*p)->InterfaceID = InterfaceID; + LogInfo("mDNS_AddSearchDomain created new %##s, InterfaceID %p", domain->c, InterfaceID); + } +} + +mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + (void)m; // unused + if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext); +} + +mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + SearchListElem *slElem = question->QuestionContext; + mStatus err; + const char *name; + + if (answer->rrtype != kDNSType_PTR) return; + if (answer->RecordType == kDNSRecordTypePacketNegative) return; + if (answer->InterfaceID == mDNSInterface_LocalOnly) return; + + if (question == &slElem->BrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse]; + else if (question == &slElem->DefBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]; + else if (question == &slElem->AutomaticBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic]; + else if (question == &slElem->RegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration]; + else if (question == &slElem->DefRegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault]; + else { LogMsg("FoundDomain - unknown question"); return; } + + LogInfo("FoundDomain: %p %s %s Q %##s A %s", answer->InterfaceID, AddRecord ? "Add" : "Rmv", name, question->qname.c, RRDisplayString(m, answer)); + + if (AddRecord) + { + ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem)); + if (!arElem) { LogMsg("ERROR: FoundDomain out of memory"); return; } + mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, arElem); + MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name); + AppendDNSNameString (&arElem->ar.namestorage, "local"); + AssignDomainName(&arElem->ar.resrec.rdata->u.name, &answer->rdata->u.name); + LogInfo("FoundDomain: Registering %s", ARDisplayString(m, &arElem->ar)); + err = mDNS_Register(m, &arElem->ar); + if (err) { LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); mDNSPlatformMemFree(arElem); return; } + arElem->next = slElem->AuthRecs; + slElem->AuthRecs = arElem; + } + else + { + ARListElem **ptr = &slElem->AuthRecs; + while (*ptr) + { + if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, &answer->rdata->u.name)) + { + ARListElem *dereg = *ptr; + *ptr = (*ptr)->next; + LogInfo("FoundDomain: Deregistering %s", ARDisplayString(m, &dereg->ar)); + err = mDNS_Deregister(m, &dereg->ar); + if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err); + // Memory will be freed in the FreeARElemCallback + } + else + ptr = &(*ptr)->next; + } + } +} + +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING +mDNSexport void udns_validatelists(void *const v) +{ + mDNS *const m = v; + + NATTraversalInfo *n; + for (n = m->NATTraversals; n; n=n->next) + if (n->next == (NATTraversalInfo *)~0 || n->clientCallback == (NATTraversalClientCallback) ~0) + LogMemCorruption("m->NATTraversals: %p is garbage", n); + + DNSServer *d; + for (d = m->DNSServers; d; d=d->next) + if (d->next == (DNSServer *)~0 || d->teststate > DNSServer_Disabled) + LogMemCorruption("m->DNSServers: %p is garbage (%d)", d, d->teststate); + + DomainAuthInfo *info; + for (info = m->AuthInfoList; info; info = info->next) + if (info->next == (DomainAuthInfo *)~0) + LogMemCorruption("m->AuthInfoList: %p is garbage", info); + + HostnameInfo *hi; + for (hi = m->Hostnames; hi; hi = hi->next) + if (hi->next == (HostnameInfo *)~0 || hi->StatusCallback == (mDNSRecordCallback*)~0) + LogMemCorruption("m->Hostnames: %p is garbage", n); + + SearchListElem *ptr; + for (ptr = SearchList; ptr; ptr = ptr->next) + if (ptr->next == (SearchListElem *)~0 || ptr->AuthRecs == (void*)~0) + LogMemCorruption("SearchList: %p is garbage (%X)", ptr, ptr->AuthRecs); +} +#endif + +// This should probably move to the UDS daemon -- the concept of legacy clients and automatic registration / automatic browsing +// is really a UDS API issue, not something intrinsic to uDNS + +mDNSlocal void uDNS_DeleteWABQueries(mDNS *const m, SearchListElem *ptr, int delete) +{ + const char *name1 = mDNSNULL; + const char *name2 = mDNSNULL; + ARListElem **arList = &ptr->AuthRecs; + domainname namestorage1, namestorage2; + mStatus err; + + // "delete" parameter indicates the type of query. + switch (delete) + { + case UDNS_WAB_BROWSE_QUERY: + mDNS_StopGetDomains(m, &ptr->BrowseQ); + mDNS_StopGetDomains(m, &ptr->DefBrowseQ); + name1 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse]; + name2 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]; + break; + case UDNS_WAB_LBROWSE_QUERY: + mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ); + name1 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic]; + break; + case UDNS_WAB_REG_QUERY: + mDNS_StopGetDomains(m, &ptr->RegisterQ); + mDNS_StopGetDomains(m, &ptr->DefRegisterQ); + name1 = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration]; + name2 = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault]; + break; + default: + LogMsg("uDNS_DeleteWABQueries: ERROR!! returning from default"); + return; + } + // When we get the results to the domain enumeration queries, we add a LocalOnly + // entry. For example, if we issue a domain enumeration query for b._dns-sd._udp.xxxx.com, + // and when we get a response, we add a LocalOnly entry b._dns-sd._udp.local whose RDATA + // points to what we got in the response. Locate the appropriate LocalOnly entries and delete + // them. + if (name1) + { + MakeDomainNameFromDNSNameString(&namestorage1, name1); + AppendDNSNameString(&namestorage1, "local"); + } + if (name2) + { + MakeDomainNameFromDNSNameString(&namestorage2, name2); + AppendDNSNameString(&namestorage2, "local"); + } + while (*arList) + { + ARListElem *dereg = *arList; + if ((name1 && SameDomainName(&dereg->ar.namestorage, &namestorage1)) || + (name2 && SameDomainName(&dereg->ar.namestorage, &namestorage2))) + { + LogInfo("uDNS_DeleteWABQueries: Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); + *arList = dereg->next; + err = mDNS_Deregister(m, &dereg->ar); + if (err) LogMsg("uDNS_DeleteWABQueries:: ERROR!! mDNS_Deregister returned %d", err); + // Memory will be freed in the FreeARElemCallback + } + else + { + LogInfo("uDNS_DeleteWABQueries: Skipping PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); + arList = &(*arList)->next; + } + } +} + +mDNSexport void uDNS_SetupWABQueries(mDNS *const m) +{ + SearchListElem **p = &SearchList, *ptr; + mStatus err; + int action = 0; + + // step 1: mark each element for removal + for (ptr = SearchList; ptr; ptr = ptr->next) + ptr->flag |= SLE_DELETE; + + // Make sure we have the search domains from the platform layer so that if we start the WAB + // queries below, we have the latest information. + mDNS_Lock(m); + if (!mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL, mDNSfalse)) + { + // If the configuration did not change, clear the flag so that we don't free the searchlist. + // We still have to start the domain enumeration queries as we may not have started them + // before. + for (ptr = SearchList; ptr; ptr = ptr->next) + ptr->flag &= ~SLE_DELETE; + LogInfo("uDNS_SetupWABQueries: No config change"); + } + mDNS_Unlock(m); + + if (m->WABBrowseQueriesCount) + action |= UDNS_WAB_BROWSE_QUERY; + if (m->WABLBrowseQueriesCount) + action |= UDNS_WAB_LBROWSE_QUERY; + if (m->WABRegQueriesCount) + action |= UDNS_WAB_REG_QUERY; + + + // delete elems marked for removal, do queries for elems marked add + while (*p) + { + ptr = *p; + LogInfo("uDNS_SetupWABQueries:action 0x%x: Flags 0x%x, AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c); + // If SLE_DELETE is set, stop all the queries, deregister all the records and free the memory. + // Otherwise, check to see what the "action" requires. If a particular action bit is not set and + // we have started the corresponding queries as indicated by the "flags", stop those queries and + // deregister the records corresponding to them. + if ((ptr->flag & SLE_DELETE) || + (!(action & UDNS_WAB_BROWSE_QUERY) && (ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED)) || + (!(action & UDNS_WAB_LBROWSE_QUERY) && (ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED)) || + (!(action & UDNS_WAB_REG_QUERY) && (ptr->flag & SLE_WAB_REG_QUERY_STARTED))) + { + if (ptr->flag & SLE_DELETE) + { + ARListElem *arList = ptr->AuthRecs; + ptr->AuthRecs = mDNSNULL; + *p = ptr->next; + + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries + // We suppressed the domain enumeration for scoped search domains below. When we enable that + // enable this. + if ((ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: DELETE Browse for domain %##s", ptr->domain.c); + mDNS_StopGetDomains(m, &ptr->BrowseQ); + mDNS_StopGetDomains(m, &ptr->DefBrowseQ); + } + if ((ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: DELETE Legacy Browse for domain %##s", ptr->domain.c); + mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ); + } + if ((ptr->flag & SLE_WAB_REG_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: DELETE Registration for domain %##s", ptr->domain.c); + mDNS_StopGetDomains(m, &ptr->RegisterQ); + mDNS_StopGetDomains(m, &ptr->DefRegisterQ); + } + + mDNSPlatformMemFree(ptr); + + // deregister records generated from answers to the query + while (arList) + { + ARListElem *dereg = arList; + arList = arList->next; + LogInfo("uDNS_SetupWABQueries: DELETE Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); + err = mDNS_Deregister(m, &dereg->ar); + if (err) LogMsg("uDNS_SetupWABQueries:: ERROR!! mDNS_Deregister returned %d", err); + // Memory will be freed in the FreeARElemCallback + } + continue; + } + + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries + // We suppressed the domain enumeration for scoped search domains below. When we enable that + // enable this. + if (!(action & UDNS_WAB_BROWSE_QUERY) && (ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: Deleting Browse for domain %##s", ptr->domain.c); + ptr->flag &= ~SLE_WAB_BROWSE_QUERY_STARTED; + uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_BROWSE_QUERY); + } + + if (!(action & UDNS_WAB_LBROWSE_QUERY) && (ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: Deleting Legacy Browse for domain %##s", ptr->domain.c); + ptr->flag &= ~SLE_WAB_LBROWSE_QUERY_STARTED; + uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_LBROWSE_QUERY); + } + + if (!(action & UDNS_WAB_REG_QUERY) && (ptr->flag & SLE_WAB_REG_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: Deleting Registration for domain %##s", ptr->domain.c); + ptr->flag &= ~SLE_WAB_REG_QUERY_STARTED; + uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_REG_QUERY); + } + + // Fall through to handle the ADDs + } + + if ((action & UDNS_WAB_BROWSE_QUERY) && !(ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED)) + { + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. + // Also, suppress the domain enumeration for scoped search domains for now until there is a need. + if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + mStatus err1, err2; + err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err1) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeBrowse)\n", ptr->domain.c, err1); + } + else + { + LogInfo("uDNS_SetupWABQueries: Starting Browse for domain %##s", ptr->domain.c); + } + err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err2) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeBrowseDefault)\n", ptr->domain.c, err2); + } + else + { + LogInfo("uDNS_SetupWABQueries: Starting Default Browse for domain %##s", ptr->domain.c); + } + // For simplicity, we mark a single bit for denoting that both the browse queries have started. + // It is not clear as to why one would fail to start and the other would succeed in starting up. + // If that happens, we will try to stop both the queries and one of them won't be in the list and + // it is not a hard error. + if (!err1 || !err2) + { + ptr->flag |= SLE_WAB_BROWSE_QUERY_STARTED; + } + } + } + if ((action & UDNS_WAB_LBROWSE_QUERY) && !(ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED)) + { + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. + // Also, suppress the domain enumeration for scoped search domains for now until there is a need. + if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + mStatus err1; + err1 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err1) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeBrowseAutomatic)\n", + ptr->domain.c, err1); + } + else + { + ptr->flag |= SLE_WAB_LBROWSE_QUERY_STARTED; + LogInfo("uDNS_SetupWABQueries: Starting Legacy Browse for domain %##s", ptr->domain.c); + } + } + } + if ((action & UDNS_WAB_REG_QUERY) && !(ptr->flag & SLE_WAB_REG_QUERY_STARTED)) + { + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. + // Also, suppress the domain enumeration for scoped search domains for now until there is a need. + if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + mStatus err1, err2; + err1 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err1) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeRegistration)\n", ptr->domain.c, err1); + } + else + { + LogInfo("uDNS_SetupWABQueries: Starting Registration for domain %##s", ptr->domain.c); + } + err2 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err2) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeRegistrationDefault)", ptr->domain.c, err2); + } + else + { + LogInfo("uDNS_SetupWABQueries: Starting Default Registration for domain %##s", ptr->domain.c); + } + if (!err1 || !err2) + { + ptr->flag |= SLE_WAB_REG_QUERY_STARTED; + } + } + } + + p = &ptr->next; + } +} + +// mDNS_StartWABQueries is called once per API invocation where normally +// one of the bits is set. +mDNSexport void uDNS_StartWABQueries(mDNS *const m, int queryType) +{ + if (queryType & UDNS_WAB_BROWSE_QUERY) + { + m->WABBrowseQueriesCount++; + LogInfo("uDNS_StartWABQueries: Browse query count %d", m->WABBrowseQueriesCount); + } + if (queryType & UDNS_WAB_LBROWSE_QUERY) + { + m->WABLBrowseQueriesCount++; + LogInfo("uDNS_StartWABQueries: Legacy Browse query count %d", m->WABLBrowseQueriesCount); + } + if (queryType & UDNS_WAB_REG_QUERY) + { + m->WABRegQueriesCount++; + LogInfo("uDNS_StartWABQueries: Reg query count %d", m->WABRegQueriesCount); + } + uDNS_SetupWABQueries(m); +} + +// mDNS_StopWABQueries is called once per API invocation where normally +// one of the bits is set. +mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType) +{ + if (queryType & UDNS_WAB_BROWSE_QUERY) + { + m->WABBrowseQueriesCount--; + LogInfo("uDNS_StopWABQueries: Browse query count %d", m->WABBrowseQueriesCount); + } + if (queryType & UDNS_WAB_LBROWSE_QUERY) + { + m->WABLBrowseQueriesCount--; + LogInfo("uDNS_StopWABQueries: Legacy Browse query count %d", m->WABLBrowseQueriesCount); + } + if (queryType & UDNS_WAB_REG_QUERY) + { + m->WABRegQueriesCount--; + LogInfo("uDNS_StopWABQueries: Reg query count %d", m->WABRegQueriesCount); + } + uDNS_SetupWABQueries(m); +} + +mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal) +{ + SearchListElem *p = SearchList; + int count = *searchIndex; + (void) m; // unused + + if (count < 0) { LogMsg("uDNS_GetNextSearchDomain: count %d less than zero", count); return mDNSNULL; } + + // Skip the domains that we already looked at before. Guard against "p" + // being NULL. When search domains change we may not set the SearchListIndex + // of the question to zero immediately e.g., domain enumeration query calls + // uDNS_SetupWABQueries which reads in the new search domain but does not + // restart the questions immediately. Questions are restarted as part of + // network change and hence temporarily SearchListIndex may be out of range. + + for (; count && p; count--) + p = p->next; + + while (p) + { + int labels = CountLabels(&p->domain); + if (labels > 0) + { + const domainname *d = SkipLeadingLabels(&p->domain, labels - 1); + if (SameDomainLabel(d->c, (const mDNSu8 *)"\x4" "arpa")) + { + LogInfo("uDNS_GetNextSearchDomain: skipping search domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + (*searchIndex)++; + p = p->next; + continue; + } + if (ignoreDotLocal && SameDomainLabel(d->c, (const mDNSu8 *)"\x5" "local")) + { + LogInfo("uDNS_GetNextSearchDomain: skipping local domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + (*searchIndex)++; + p = p->next; + continue; + } + } + // Point to the next one in the list which we will look at next time. + (*searchIndex)++; + // When we are appending search domains in a ActiveDirectory domain, the question's InterfaceID + // set to mDNSInterface_Unicast. Match the unscoped entries in that case. + if (((InterfaceID == mDNSInterface_Unicast) && (p->InterfaceID == mDNSInterface_Any)) || + p->InterfaceID == InterfaceID) + { + LogInfo("uDNS_GetNextSearchDomain returning domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + return &p->domain; + } + LogInfo("uDNS_GetNextSearchDomain skipping domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + p = p->next; + } + return mDNSNULL; +} + +mDNSlocal void FlushAddressCacheRecords(mDNS *const m) +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.InterfaceID) continue; + + // If a resource record can answer A or AAAA, they need to be flushed so that we will + // deliver an ADD or RMV + if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) || + RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA)) + { + LogInfo("FlushAddressCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr)); + mDNS_PurgeCacheResourceRecord(m, cr); + } + } +} + +// Retry questions which has seach domains appended +mDNSexport void RetrySearchDomainQuestions(mDNS *const m) +{ + DNSQuestion *q; + mDNSBool found = mDNSfalse; + + // Check to see if there are any questions which needs search domains to be applied. + // If there is none, search domains can't possibly affect them. + for (q = m->Questions; q; q = q->next) + { + if (q->AppendSearchDomains) + { + found = mDNStrue; + break; + } + } + if (!found) + { + LogInfo("RetrySearchDomainQuestions: Questions with AppendSearchDomain not found"); + return; + } + LogInfo("RetrySearchDomainQuestions: Question with AppendSearchDomain found %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + // Purge all the A/AAAA cache records and restart the queries. mDNSCoreRestartAddressQueries + // does this. When we restart the question, we first want to try the new search domains rather + // than use the entries that is already in the cache. When we appended search domains, we might + // have created cache entries which is no longer valid as there are new search domains now + mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL); +} + +// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows: +// 1) query for b._dns-sd._udp.local on LocalOnly interface +// (.local manually generated via explicit callback) +// 2) for each search domain (from prefs pane), query for b._dns-sd._udp.<searchdomain>. +// 3) for each result from (2), register LocalOnly PTR record b._dns-sd._udp.local. -> <result> +// 4) result above should generate a callback from question in (1). result added to global list +// 5) global list delivered to client via GetSearchDomainList() +// 6) client calls to enumerate domains now go over LocalOnly interface +// (!!!KRS may add outgoing interface in addition) + +struct CompileTimeAssertionChecks_uDNS +{ + // Check our structures are reasonable sizes. Including overly-large buffers, or embedding + // other overly-large structures instead of having a pointer to them, can inadvertently + // cause structure sizes (and therefore memory usage) to balloon unreasonably. + char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1]; + char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1]; +}; + +#else // !UNICAST_DISABLED + +mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr) +{ + (void) m; + (void) rr; + + return mDNSNULL; +} + +mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name) +{ + (void) m; + (void) name; + + return mDNSNULL; +} + +mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) +{ + (void) m; + (void) q; + + return mDNSNULL; +} + +mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) +{ + (void) m; + (void) q; +} + +mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp) +{ + (void) tcp; +} + +mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) +{ + (void) m; + (void) traversal; + + return mStatus_UnsupportedErr; +} + +mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) +{ + (void) m; + (void) traversal; + + return mStatus_UnsupportedErr; +} + +mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) +{ + (void) m; + (void) q; +} + +mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext) +{ + (void) m; + (void) name; + (void) target; + (void) callback; + (void) ZoneDataContext; + + return mDNSNULL; +} + +mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData) +{ + (void) m; + (void) err; + (void) zoneData; +} + +mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion) +{ + (void) m; + (void) msg; + (void) end; + (void) srcaddr; + (void) srcport; + (void) matchQuestion; + + return uDNS_LLQ_Not; +} + +mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags) +{ + (void) m; + (void) q; + (void) responseFlags; +} + +mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) +{ + (void) domain; + (void) InterfaceID; +} + +mDNSexport void RetrySearchDomainQuestions(mDNS *const m) +{ + (void) m; +} + +mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel) +{ + (void) m; + (void) info; + (void) domain; + (void) keyname; + (void) b64keydata; + (void) hostname; + (void) port; + (void) autoTunnel; + + return mStatus_UnsupportedErr; +} + +mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal) +{ + (void) m; + (void) InterfaceID; + (void) searchIndex; + (void) ignoreDotLocal; + + return mDNSNULL; +} + +mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name) +{ + (void) m; + (void) name; + + return mDNSNULL; +} + +mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal) +{ + (void) m; + (void) traversal; + + return mStatus_UnsupportedErr; +} + +mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal) +{ + (void) m; + (void) traversal; + + return mStatus_UnsupportedErr; +} + +mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr, + const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, + mDNSBool reqAAAA, mDNSBool reqDO) +{ + (void) m; + (void) d; + (void) interface; + (void) serviceID; + (void) addr; + (void) port; + (void) scoped; + (void) timeout; + (void) cellIntf; + (void) resGroupID; + (void) reqA; + (void) reqAAAA; + (void) reqDO; + + return mDNSNULL; +} + +mDNSexport void uDNS_SetupWABQueries(mDNS *const m) +{ + (void) m; +} + +mDNSexport void uDNS_StartWABQueries(mDNS *const m, int queryType) +{ + (void) m; + (void) queryType; +} + +mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType) +{ + (void) m; + (void) queryType; +} + +mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext) +{ + (void) m; + (void) fqdn; + (void) StatusCallback; + (void) StatusContext; +} +mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router) +{ + (void) m; + (void) v4addr; + (void) v6addr; + (void) router; +} + +mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) +{ + (void) m; + (void) fqdn; +} + +mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks) +{ + (void) m; + (void) waitTicks; +} + +mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q) +{ + (void)q; + + return mDNSfalse; +} + +#endif // !UNICAST_DISABLED diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h index 2a6a9edf2b..eca8b70152 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h @@ -1,192 +1,151 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 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" { +#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 + // which typically heal quickly, so we start agressively and exponentially back off #define MAX_UCAST_POLL_INTERVAL (60 * 60 * mDNSPlatformOneSecond) +//#define MAX_UCAST_POLL_INTERVAL (1 * 60 * mDNSPlatformOneSecond) #define LLQ_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc. #define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond) // require server responses within one minute of request -#define UPDATE_PORT_NAME "_dns-update._udp." -#define LLQ_PORT_NAME "_dns-llq._udp" +#define MAX_DNSSEC_UNANSWERED_QUERIES 1 // number of unanswered queries from any one uDNS server before turning off DNSSEC Validation +#define MAX_UCAST_UNANSWERED_QUERIES 2 // number of unanswered queries from any one uDNS server before trying another server +#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server + +// On some interfaces, we want to delay the first retransmission to a minimum of 2 seconds +// rather than the default (1 second). +#define MIN_UCAST_RETRANS_TIMEOUT (2 * mDNSPlatformOneSecond) + #define DEFAULT_UPDATE_LEASE 7200 - + +#define QuestionIntervalStep 3 +#define QuestionIntervalStep2 (QuestionIntervalStep*QuestionIntervalStep) +#define QuestionIntervalStep3 (QuestionIntervalStep*QuestionIntervalStep*QuestionIntervalStep) +#define InitialQuestionInterval ((mDNSPlatformOneSecond + QuestionIntervalStep-1) / QuestionIntervalStep) +#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) + +// just move to MaxQuestionInterval once over this threshold +#define QuestionIntervalThreshold (QuestionIntervalStep3 * mDNSPlatformOneSecond) + +// For Unicast record registrations, we initialize the interval to 1 second. When we send any query for +// the record registration e.g., GetZoneData, we always back off by QuestionIntervalStep +// so that the first retry does not happen until 3 seconds which should be enough for TCP/TLS to be done. +#define INIT_RECORD_REG_INTERVAL (1 * mDNSPlatformOneSecond) +#define MAX_RECORD_REG_INTERVAL (15 * 60 * mDNSPlatformOneSecond) +#define MERGE_DELAY_TIME (1 * mDNSPlatformOneSecond) + +// If we are refreshing, we do it at least 5 times with a min update frequency of +// 5 minutes +#define MAX_UPDATE_REFRESH_COUNT 5 +#define MIN_UPDATE_REFRESH_TIME (5 * 60 * mDNSPlatformOneSecond) + +// For questions that use kDNSServiceFlagsTimeout and we don't have a matching resolver e.g., no dns servers, +// then use the default value of 30 seconds +#define DEFAULT_UDNS_TIMEOUT 30 // in seconds + +// For questions that are validating responses (q->ValidatingResponse == 1), use 10 seconds +// which accomodates two DNS servers and two queries per DNS server. +#define DEFAULT_UDNSSEC_TIMEOUT 10 // in seconds + +// If we are sending queries with EDNS0/DO option and we have no indications that the server +// is DNSSEC aware and we have already reached MAX_DNSSEC_RETRANSMISSIONS, we disable +// validation (for optional case only) for any questions that uses this server +#define MAX_DNSSEC_RETRANSMISSIONS 3 + // 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 - +extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo); +extern void startLLQHandshake(mDNS *m, DNSQuestion *q); +extern void sendLLQRefresh(mDNS *m, DNSQuestion *q); + +extern void SleepRecordRegistrations(mDNS *m); + // uDNS_UpdateRecord // following fields must be set, and the update validated, upon entry. // rr->NewRData // rr->newrdlength // rr->UpdateCallback -extern mStatus uDNS_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); +extern void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q); +extern mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr); +extern mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt); +extern mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal); + +extern void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData); +extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr); +extern const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr); +extern void uDNS_CheckCurrentQuestion(mDNS *const m); // integer fields of msg header must be in HOST byte order before calling this routine extern void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, 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 - } + const mDNSAddr *const srcaddr, const mDNSIPPort srcport); + +extern void uDNS_Tasks(mDNS *const m); +extern void UpdateAllSRVRecords(mDNS *m); +extern void CheckNATMappings(mDNS *m); + +extern mStatus uDNS_SetupDNSConfig(mDNS *const m); + +// uDNS_SetupWABQueries reads search domains from the platform layer and starts the Wide Area Bonjour +// (WAB) domain enumeration queries if necessary. + +#define UDNS_WAB_BROWSE_QUERY 0x00000001 // Browse queries (b, db) +#define UDNS_WAB_LBROWSE_QUERY 0x00000002 // Browse queries (lb) +#define UDNS_WAB_REG_QUERY 0x00000004 // Registration queries (r and dr) + +extern void uDNS_SetupWABQueries(mDNS *const m); +extern void uDNS_StartWABQueries(mDNS *const m, int queryType); +extern void uDNS_StopWABQueries(mDNS *const m, int queryType); +extern domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal); + +typedef enum +{ + uDNS_LLQ_Not = 0, // Normal uDNS answer: Flush any stale records from cache, and respect record TTL + uDNS_LLQ_Ignore, // LLQ initial challenge packet: ignore -- has no useful records for us + uDNS_LLQ_Entire, // LLQ initial set of answers: Flush any stale records from cache, but assume TTL is 2 x LLQ refresh interval + uDNS_LLQ_Events // LLQ event packet: don't flush cache; assume TTL is 2 x LLQ refresh interval +} uDNS_LLQType; + +extern uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion); +extern DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name); +extern DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q); +extern void DisposeTCPConn(struct tcpInfo_t *tcp); + +// NAT traversal +extern void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received PCP or NAT-PMP packet +extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr); +extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol); + +#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 index 4351e0edde..613d78552e 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c @@ -1,3695 +1,6225 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2013 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 +#if defined(_WIN32) +#include <process.h> +#define usleep(X) Sleep(((X)+999)/1000) +#else +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif -Revision 1.201 2006/06/29 03:02:47 cheshire -<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support +#include <stdlib.h> +#include <stdio.h> -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 +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" +#include "uDNS.h" +#include "uds_daemon.h" -Revision 1.199 2006/06/28 08:53:39 cheshire -Added (commented out) debugging messages +// Normally we append search domains only for queries with a single label that are not +// fully qualified. This can be overridden to apply search domains for queries (that are +// not fully qualified) with any number of labels e.g., moon, moon.cs, moon.cs.be, etc. +mDNSBool AlwaysAppendSearchDomains = mDNSfalse; -Revision 1.198 2006/06/27 20:16:07 cheshire -Fix code layout +// Apple-specific functionality, not required for other platforms +#if APPLE_OSX_mDNSResponder +#include <sys/ucred.h> +#ifndef PID_FILE +#define PID_FILE "" +#endif +#endif -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) +#ifdef LOCAL_PEERPID +#include <sys/un.h> // for LOCAL_PEERPID +#include <sys/socket.h> // for getsockopt +#include <sys/proc_info.h> // for struct proc_bsdshortinfo +#include <libproc.h> // for proc_pidinfo() +#endif //LOCAL_PEERPID +//upto 16 characters of process name (defined in <sys/proc.h> but we do not want to include that file) +#define MAXCOMLEN 16 -Revision 1.196 2006/05/05 07:07:13 cheshire -<rdar://problem/4538206> mDNSResponder fails when UDS reads deliver partial data +#if APPLE_OSX_mDNSResponder +#include <WebFilterDNS/WebFilterDNS.h> -Revision 1.195 2006/04/25 20:56:28 mkrochma -Added comment about previous checkin +#if !NO_WCF -Revision 1.194 2006/04/25 18:29:36 mkrochma -Workaround for warning: unused variable 'status' when building mDNSPosix +int WCFIsServerRunning(WCFConnection *conn) __attribute__((weak_import)); +int WCFNameResolvesToAddr(WCFConnection *conn, char* domainName, struct sockaddr* address, uid_t userid) __attribute__((weak_import)); +int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid_t userid) __attribute__((weak_import)); -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 +// Do we really need to define a macro for "if"? +#define CHECK_WCF_FUNCTION(X) if (X) +#endif // ! NO_WCF -Revision 1.192 2006/03/18 20:58:32 cheshire -Misplaced curly brace +#else +#define NO_WCF 1 +#endif // APPLE_OSX_mDNSResponder -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 +// User IDs 0-500 are system-wide processes, not actual users in the usual sense +// User IDs for real user accounts start at 501 and count up from there +#define SystemUID(X) ((X) <= 500) -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 +#define MAX_ANONYMOUS_DATA 256 -Revision 1.189 2006/01/06 00:56:31 cheshire -<rdar://problem/4400573> Should remove PID file on exit +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Types and Data Structures +#endif -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 +typedef enum +{ + t_uninitialized, + t_morecoming, + t_complete, + t_error, + t_terminated +} transfer_state; -Revision 1.187 2005/10/11 20:30:27 cheshire -<rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c +typedef struct request_state request_state; -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. +typedef void (*req_termination_fn)(request_state *request); -Revision 1.185 2005/07/29 00:55:10 ksekar -Removed validation check in uds_validatelists which generated false alarms +typedef struct registered_record_entry +{ + struct registered_record_entry *next; + mDNSu32 key; + client_context_t regrec_client_context; + request_state *request; + mDNSBool external_advertise; + mDNSInterfaceID origInterfaceID; + AuthRecord *rr; // Pointer to variable-sized AuthRecord (Why a pointer? Why not just embed it here?) +} registered_record_entry; -Revision 1.184 2005/07/04 22:40:26 cheshire -Additional debugging code to help catch memory corruption +// A single registered service: ServiceRecordSet + bookkeeping +// Note that we duplicate some fields from parent service_info object +// to facilitate cleanup, when instances and parent may be deallocated at different times. +typedef struct service_instance +{ + struct service_instance *next; + request_state *request; + AuthRecord *subtypes; + mDNSBool renameonmemfree; // Set on config change when we deregister original name + mDNSBool clientnotified; // Has client been notified of successful registration yet? + mDNSBool default_local; // is this the "local." from an empty-string registration? + mDNSBool external_advertise; // is this is being advertised externally? + domainname domain; + ServiceRecordSet srs; // note -- variable-sized object -- must be last field in struct +} service_instance; -Revision 1.183 2005/06/13 22:39:11 cheshire -<rdar://problem/4144870> Missing return statement in handle_enum_request() error handling +// for multi-domain default browsing +typedef struct browser_t +{ + struct browser_t *next; + domainname domain; + DNSQuestion q; +} browser_t; -Revision 1.182 2005/03/21 00:39:31 shersche -<rdar://problem/4021486> Fix build warnings on Win32 platform +#ifdef _WIN32 + typedef unsigned int pid_t; + typedef unsigned int socklen_t; +#endif -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. +struct request_state +{ + request_state *next; + request_state *primary; // If this operation is on a shared socket, pointer to primary + // request_state for the original DNSServiceCreateConnection() operation + dnssd_sock_t sd; + pid_t process_id; // Client's PID value + char pid_name[MAXCOMLEN]; // Client's process name + char uuid[UUID_SIZE]; + mDNSBool validUUID; + dnssd_sock_t errsd; + mDNSu32 uid; + void * platform_data; + + // Note: On a shared connection these fields in the primary structure, including hdr, are re-used + // for each new request. This is because, until we've read the ipc_msg_hdr to find out what the + // operation is, we don't know if we're going to need to allocate a new request_state or not. + transfer_state ts; + mDNSu32 hdr_bytes; // bytes of header already read + ipc_msg_hdr hdr; + mDNSu32 data_bytes; // bytes of message data already read + char *msgbuf; // pointer to data storage to pass to free() + const char *msgptr; // pointer to data to be read from (may be modified) + char *msgend; // pointer to byte after last byte of message -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. + // reply, termination, error, and client context info + int no_reply; // don't send asynchronous replies to client + mDNSs32 time_blocked; // record time of a blocked client + int unresponsiveness_reports; + struct reply_state *replies; // corresponding (active) reply list + req_termination_fn terminate; + DNSServiceFlags flags; -Revision 1.179 2005/03/04 02:47:26 ksekar -<rdar://problem/4026393> SCPreference domains disappear from enumeration when moving out from firewall + union + { + registered_record_entry *reg_recs; // list of registrations for a connection-oriented request + struct + { + mDNSInterfaceID interface_id; + mDNSBool default_domain; + mDNSBool ForceMCast; + domainname regtype; + browser_t *browsers; + const mDNSu8 *AnonData; + } browser; + struct + { + mDNSInterfaceID InterfaceID; + mDNSu16 txtlen; + void *txtdata; + mDNSIPPort port; + domainlabel name; + char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; + domainname type; + mDNSBool default_domain; + domainname host; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if this client wants us to automatically rename on conflict + mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? + int num_subtypes; + mDNSBool AnonData; + service_instance *instances; + } servicereg; + struct + { + mDNSInterfaceID interface_id; + mDNSu32 flags; + mDNSu32 protocol; + DNSQuestion q4; + DNSQuestion *q42; + DNSQuestion q6; + DNSQuestion *q62; + mDNSu8 v4ans; + mDNSu8 v6ans; + } addrinfo; + struct + { + mDNSIPPort ReqExt; // External port we originally requested, for logging purposes + NATTraversalInfo NATinfo; + } pm; + struct + { + DNSServiceFlags flags; + DNSQuestion q_all; + DNSQuestion q_default; + } enumeration; + struct + { + DNSQuestion q; + DNSQuestion *q2; + mDNSu8 ans; + } queryrecord; + struct + { + DNSQuestion qtxt; + DNSQuestion qsrv; + const ResourceRecord *txt; + const ResourceRecord *srv; + mDNSs32 ReportTime; + mDNSBool external_advertise; + } resolve; + } u; +}; -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 +// struct physically sits between ipc message header and call-specific fields in the message buffer +typedef struct +{ + DNSServiceFlags flags; // Note: This field is in NETWORK byte order + mDNSu32 ifi; // Note: This field is in NETWORK byte order + DNSServiceErrorType error; // Note: This field is in NETWORK byte order +} reply_hdr; -Revision 1.177 2005/02/25 03:05:41 cheshire -Change "broken pipe" message to debugf() +typedef struct reply_state +{ + struct reply_state *next; // If there are multiple unsent replies + mDNSu32 totallen; + mDNSu32 nwriten; + ipc_msg_hdr mhdr[1]; + reply_hdr rhdr[1]; +} reply_state; + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Globals +#endif -Revision 1.176 2005/02/24 18:44:45 ksekar -<rdar://problem/4018516> Printer Sharing does not get re-registered with wide-area +// globals +mDNSexport mDNS mDNSStorage; +mDNSexport const char ProgramName[] = "mDNSResponder"; + +static dnssd_sock_t listenfd = dnssd_InvalidSocket; +static request_state *all_requests = NULL; +#ifdef LOCAL_PEERPID +struct proc_bsdshortinfo proc; +#endif //LOCAL_PEERPID +mDNSlocal void set_peer_pid(request_state *request); +mDNSlocal void LogMcastClientInfo(request_state *req); +mDNSlocal void GetMcastClients(request_state *req); +static mDNSu32 mcount; // tracks the current active mcast operations for McastLogging +static mDNSu32 i_mcount; // sets mcount when McastLogging is enabled(PROF signal is sent) +static mDNSu32 n_mrecords; // tracks the current active mcast records for McastLogging +static mDNSu32 n_mquests; // tracks the current active mcast questions for McastLogging + +// Note asymmetry here between registration and browsing. +// For service registrations we only automatically register in domains that explicitly appear in local configuration data +// (so AutoRegistrationDomains could equally well be called SCPrefRegDomains) +// For service browsing we also learn automatic browsing domains from the network, so for that case we have: +// 1. SCPrefBrowseDomains (local configuration data) +// 2. LocalDomainEnumRecords (locally-generated local-only PTR records -- equivalent to slElem->AuthRecs in uDNS.c) +// 3. AutoBrowseDomains, which is populated by tracking add/rmv events in AutomaticBrowseDomainChange, the callback function for our mDNS_GetDomains call. +// By creating and removing our own LocalDomainEnumRecords, we trigger AutomaticBrowseDomainChange callbacks just like domains learned from the network would. + +mDNSexport DNameListElem *AutoRegistrationDomains; // Domains where we automatically register for empty-string registrations + +static DNameListElem *SCPrefBrowseDomains; // List of automatic browsing domains read from SCPreferences for "empty string" browsing +static ARListElem *LocalDomainEnumRecords; // List of locally-generated PTR records to augment those we learn from the network +mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-only PTR records plus records we get from the network + +#define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee + // n get_string() calls w/o buffer overrun +// initialization, setup/teardown functions -Revision 1.175 2005/02/21 21:31:25 ksekar -<rdar://problem/4015162> changed LogMsg to debugf +// If a platform specifies its own PID file name, we use that +#ifndef PID_FILE +#define PID_FILE "/var/run/mDNSResponder.pid" +#endif -Revision 1.174 2005/02/20 01:41:17 cheshire -Fix compiler signed/unsigned warning +mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen); -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 +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Utility Functions +#endif -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 +mDNSlocal void FatalError(char *errmsg) +{ + char* ptr = NULL; + LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno)); + *ptr = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does + abort(); // On platforms where writing to zero doesn't generate an exception, abort instead +} + +mDNSlocal mDNSu32 dnssd_htonl(mDNSu32 l) +{ + mDNSu32 ret; + char *data = (char*) &ret; + put_uint32(l, &data); + return ret; +} + +// hack to search-replace perror's to LogMsg's +mDNSlocal void my_perror(char *errmsg) +{ + LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno)); +} + +// Throttled version of my_perror: Logs once every 250 msgs +mDNSlocal void my_throttled_perror(char *err_msg) +{ + static int uds_throttle_count = 0; + if ((uds_throttle_count++ % 250) == 0) + my_perror(err_msg); +} + +// LogMcastQuestion/LogMcastQ should be called after the DNSQuestion struct is initialized(especially for q->TargetQID) +// Hence all calls are made after mDNS_StartQuery()/mDNS_StopQuery()/mDNS_StopBrowse() is called. +mDNSlocal void LogMcastQuestion(mDNS *const m, const DNSQuestion *const q, request_state *req, q_state status) +{ + if (mDNSOpaque16IsZero(q->TargetQID)) // Check for Mcast Query + { + mDNSBool mflag = mDNSfalse; + if (status == q_start) + { + if (++mcount == 1) + mflag = mDNStrue; + } + else + { + mcount--; + } + LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Question" : "-Question", q->qname.c, DNSTypeName(q->qtype), + q->InterfaceID == mDNSInterface_LocalOnly ? "lo" : q->InterfaceID == mDNSInterface_P2P ? "p2p" : + q->InterfaceID == mDNSInterface_Any ? "any" : InterfaceNameForID(m, q->InterfaceID), + req->process_id, req->pid_name); + LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse); + } + return; +} -Revision 1.171 2005/02/18 00:43:12 cheshire -<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long +// LogMcastService/LogMcastS should be called after the AuthRecord struct is initialized +// Hence all calls are made after mDNS_Register()/ just before mDNS_Deregister() +mDNSlocal void LogMcastService(mDNS *const m, const AuthRecord *const ar, request_state *req, reg_state status) +{ + if (!AuthRecord_uDNS(ar)) // Check for Mcast Service + { + mDNSBool mflag = mDNSfalse; + if (status == reg_start) + { + if (++mcount == 1) + mflag = mDNStrue; + } + else + { + mcount--; + } + LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Service" : "-Service", ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), + ar->resrec.InterfaceID == mDNSInterface_LocalOnly ? "lo" : ar->resrec.InterfaceID == mDNSInterface_P2P ? "p2p" : + ar->resrec.InterfaceID == mDNSInterface_Any ? "all" : InterfaceNameForID(m, ar->resrec.InterfaceID), + req->process_id, req->pid_name); + LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse); + } + return; +} -Revision 1.170 2005/02/16 01:15:02 cheshire -Improve LogOperation() debugging messages for DNSServiceBrowse and DNSServiceRegister +// For complete Mcast State Log, pass mDNStrue to mstatelog in LogMcastStateInfo() +mDNSexport void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog) +{ + if (!mstatelog) + { + if (!all_requests) + { + LogMcastNoIdent("<None>"); + } + else + { + request_state *req, *r; + for (req = all_requests; req; req=req->next) + { + if (req->primary) // If this is a subbordinate operation, check that the parent is in the list + { + for (r = all_requests; r && r != req; r=r->next) + if (r == req->primary) + goto foundpar; + } + // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info + GetMcastClients(req); + foundpar:; + } + LogMcastNoIdent("--- MCAST RECORDS COUNT[%d] MCAST QUESTIONS COUNT[%d] ---", n_mrecords, n_mquests); + n_mrecords = n_mquests = 0; // Reset the values + } + } + else + { + static mDNSu32 i_mpktnum; + i_mcount = 0; + if (start) + mcount = 0; + // mcount is initialized to 0 when the PROF signal is sent since mcount could have + // wrong value if MulticastLogging is disabled and then re-enabled + LogMcastNoIdent("--- START MCAST STATE LOG ---"); + if (!all_requests) + { + mcount = 0; + LogMcastNoIdent("<None>"); + } + else + { + request_state *req, *r; + for (req = all_requests; req; req=req->next) + { + if (req->primary) // If this is a subbordinate operation, check that the parent is in the list + { + for (r = all_requests; r && r != req; r=r->next) + if (r == req->primary) + goto foundparent; + LogMcastNoIdent("%3d: Orphan operation; parent not found in request list", req->sd); + } + // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info + LogMcastClientInfo(req); + foundparent:; + } + if(!mcount) // To initially set mcount + mcount = i_mcount; + } + if (mcount == 0) + { + i_mpktnum = m->MPktNum; + LogMcastNoIdent("--- MCOUNT[%d]: IMPKTNUM[%d] ---", mcount, i_mpktnum); + } + if (mflag) + LogMcastNoIdent("--- MCOUNT[%d]: CMPKTNUM[%d] - IMPKTNUM[%d] = [%d]PKTS ---", mcount, m->MPktNum, i_mpktnum, (m->MPktNum - i_mpktnum)); + LogMcastNoIdent("--- END MCAST STATE LOG ---"); + } +} -Revision 1.169 2005/02/08 01:57:14 cheshire -More detailed error reporting in udsserver_init() +mDNSlocal void abort_request(request_state *req) +{ + if (req->terminate == (req_termination_fn) ~0) + { LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; } -Revision 1.168 2005/02/03 00:44:37 cheshire -<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL + // First stop whatever mDNSCore operation we were doing + // If this is actually a shared connection operation, then its req->terminate function will scan + // the all_requests list and terminate any subbordinate operations sharing this file descriptor + if (req->terminate) req->terminate(req); -Revision 1.167 2005/02/02 02:19:32 cheshire -Add comment explaining why unlink(MDNS_UDS_SERVERPATH); fails + if (!dnssd_SocketValid(req->sd)) + { LogMsg("abort_request: ERROR: Attempt to abort operation %p with invalid fd %d", req, req->sd); return; } -Revision 1.166 2005/02/01 19:58:52 ksekar -Shortened cryptic "broken pipe" syslog message + // Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies + if (!req->primary) + { + if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd); + else LogOperation("%3d: Removing FD", req->sd); + udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us + if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; } -Revision 1.165 2005/02/01 19:56:47 ksekar -Moved LogMsg from daemon.c to uds_daemon.c, cleaned up wording + while (req->replies) // free pending replies + { + reply_state *ptr = req->replies; + req->replies = req->replies->next; + freeL("reply_state (abort)", ptr); + } + } -Revision 1.164 2005/01/28 06:07:55 cheshire -Don't use deliver_error() from within handle_regrecord_request() + // Set req->sd to something invalid, so that udsserver_idle knows to unlink and free this structure +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING + // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses + // for detecting when the memory for an object is inadvertently freed while the object is still on some list + req->sd = req->errsd = -2; +#else + req->sd = req->errsd = dnssd_InvalidSocket; +#endif + // We also set req->terminate to a bogus value so we know if abort_request() gets called again for this request + req->terminate = (req_termination_fn) ~0; +} + +mDNSlocal void AbortUnlinkAndFree(request_state *req) +{ + request_state **p = &all_requests; + abort_request(req); + while (*p && *p != req) p=&(*p)->next; + if (*p) { *p = req->next; freeL("request_state/AbortUnlinkAndFree", req); } + else LogMsg("AbortUnlinkAndFree: ERROR: Attempt to abort operation %p not in list", req); +} + +mDNSlocal reply_state *create_reply(const reply_op_t op, const size_t datalen, request_state *const request) +{ + reply_state *reply; -Revision 1.163 2005/01/28 01:39:16 cheshire -Include file descriptor number in "broken pipe" message + if ((unsigned)datalen < sizeof(reply_hdr)) + { + LogMsg("ERROR: create_reply - data length less than length of required fields"); + return NULL; + } -Revision 1.162 2005/01/27 23:59:20 cheshire -Remove extraneous LogMsg + reply = mallocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr)); + if (!reply) FatalError("ERROR: malloc"); -Revision 1.161 2005/01/27 22:57:56 cheshire -Fix compile errors on gcc4 + reply->next = mDNSNULL; + reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr); + reply->nwriten = 0; -Revision 1.160 2005/01/27 20:52:11 cheshire -<rdar://problem/3972566> mDNSResponder leaks sockets for add/update/remove record calls + reply->mhdr->version = VERSION; + reply->mhdr->datalen = (mDNSu32)datalen; + reply->mhdr->ipc_flags = 0; + reply->mhdr->op = op; + reply->mhdr->client_context = request->hdr.client_context; + reply->mhdr->reg_index = 0; -Revision 1.159 2005/01/27 01:45:25 cheshire -<rdar://problem/3976147> mDNSResponder should never call exit(1); + return reply; +} -Revision 1.158 2005/01/25 17:28:07 ksekar -<rdar://problem/3971467> Should not return "local" twice for domain enumeration +// Append a reply to the list in a request object +// If our request is sharing a connection, then we append our reply_state onto the primary's list +mDNSlocal void append_reply(request_state *req, reply_state *rep) +{ + request_state *r = req->primary ? req->primary : req; + reply_state **ptr = &r->replies; + while (*ptr) ptr = &(*ptr)->next; + *ptr = rep; + rep->next = NULL; +} -Revision 1.157 2005/01/21 02:20:39 cheshire -Fix mistake in LogOperation() format string +// Generates a response message giving name, type, domain, plus interface index, +// suitable for a browse result or service registration result. +// On successful completion rep is set to point to a malloc'd reply_state struct +mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const mDNSInterfaceID id, + request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err) +{ + domainlabel name; + domainname type, dom; + *rep = NULL; + if (!DeconstructServiceName(servicename, &name, &type, &dom)) + return kDNSServiceErr_Invalid; + else + { + char namestr[MAX_DOMAIN_LABEL+1]; + char typestr[MAX_ESCAPED_DOMAIN_NAME]; + char domstr [MAX_ESCAPED_DOMAIN_NAME]; + int len; + char *data; + + ConvertDomainLabelToCString_unescaped(&name, namestr); + ConvertDomainNameToCString(&type, typestr); + ConvertDomainNameToCString(&dom, domstr); + + // Calculate reply data length + len = sizeof(DNSServiceFlags); + len += sizeof(mDNSu32); // if index + len += sizeof(DNSServiceErrorType); + len += (int) (strlen(namestr) + 1); + len += (int) (strlen(typestr) + 1); + len += (int) (strlen(domstr) + 1); + + // Build reply header + *rep = create_reply(op, len, request); + (*rep)->rhdr->flags = dnssd_htonl(flags); + (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse)); + (*rep)->rhdr->error = dnssd_htonl(err); + + // Build reply body + data = (char *)&(*rep)->rhdr[1]; + put_string(namestr, &data); + put_string(typestr, &data); + put_string(domstr, &data); + + return mStatus_NoError; + } +} + +// Special support to enable the DNSServiceBrowse call made by Bonjour Browser +// Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse +mDNSlocal void GenerateBonjourBrowserResponse(const domainname *const servicename, const mDNSInterfaceID id, + request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err) +{ + char namestr[MAX_DOMAIN_LABEL+1]; + char typestr[MAX_ESCAPED_DOMAIN_NAME]; + static const char domstr[] = "."; + int len; + char *data; -Revision 1.156 2005/01/19 19:15:36 ksekar -Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer + *rep = NULL; -Revision 1.155 2005/01/19 03:00:47 cheshire -Show Add/Rmv in DNSServiceBrowse LogOperation() message + // 1. Put first label in namestr + ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr); -Revision 1.154 2005/01/15 00:56:42 ksekar -<rdar://problem/3954575> Unicast services don't disappear when logging -out of VPN + // 2. Put second label and "local" into typestr + mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename)); -Revision 1.153 2005/01/14 18:44:28 ksekar -<rdar://problem/3954609> mDNSResponder is crashing when changing domains + // Calculate reply data length + len = sizeof(DNSServiceFlags); + len += sizeof(mDNSu32); // if index + len += sizeof(DNSServiceErrorType); + len += (int) (strlen(namestr) + 1); + len += (int) (strlen(typestr) + 1); + len += (int) (strlen(domstr) + 1); + + // Build reply header + *rep = create_reply(op, len, request); + (*rep)->rhdr->flags = dnssd_htonl(flags); + (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse)); + (*rep)->rhdr->error = dnssd_htonl(err); + + // Build reply body + data = (char *)&(*rep)->rhdr[1]; + put_string(namestr, &data); + put_string(typestr, &data); + put_string(domstr, &data); +} + +// Returns a resource record (allocated w/ malloc) containing the data found in an IPC message +// Data must be in the following format: flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional) ttl +// (ttl only extracted/set if ttl argument is non-zero). Returns NULL for a bad-parameter error +mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, int validate_flags) +{ + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + char name[256]; + int str_err = get_string(&request->msgptr, request->msgend, name, sizeof(name)); + mDNSu16 type = get_uint16(&request->msgptr, request->msgend); + mDNSu16 class = get_uint16(&request->msgptr, request->msgend); + mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); + const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0; + int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + AuthRecord *rr; + mDNSInterfaceID InterfaceID; + AuthRecType artype; -Revision 1.152 2005/01/13 17:16:38 ksekar -Back out checkin 1.150 - correct fix is on clientstub side + request->flags = flags; -Revision 1.151 2005/01/11 21:06:29 ksekar -Changed now-benign LogMsg to debugf + if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; } -Revision 1.150 2005/01/07 23:59:15 ksekar -<rdar://problem/3942900> dnd-sd shows the wrong port numbers + if (!request->msgptr) { LogMsg("Error reading Resource Record from client"); return NULL; } -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 + if (validate_flags && + !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) && + !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)) + { + LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)"); + return NULL; + } -Revision 1.148 2004/12/20 20:37:35 cheshire -AllowRemoteQuery not set for the extras in a ServiceRecordSet + rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size); + if (!rr) FatalError("ERROR: malloc"); -Revision 1.147 2004/12/20 00:15:41 cheshire -Include client file descriptor numbers in udsserver_info() output + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (InterfaceID == mDNSInterface_LocalOnly) + artype = AuthRecordLocalOnly; + else if (InterfaceID == mDNSInterface_P2P) + artype = AuthRecordP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P) + && (flags & kDNSServiceFlagsIncludeAWDL)) + artype = AuthRecordAnyIncludeAWDLandP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P)) + artype = AuthRecordAnyIncludeP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeAWDL)) + artype = AuthRecordAnyIncludeAWDL; + else + artype = AuthRecordAny; -Revision 1.146 2004/12/17 05:25:47 cheshire -<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0, + (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL); -Revision 1.145 2004/12/16 21:39:46 cheshire -Include CacheGroup objects in CacheUsed count + if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name)) + { + LogMsg("ERROR: bad name: %s", name); + freeL("AuthRecord/read_rr_from_ipc_msg", rr); + return NULL; + } -Revision 1.144 2004/12/16 21:27:38 ksekar -Fixed build failures when compiled with verbose debugging messages + if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue; + rr->resrec.rrclass = class; + rr->resrec.rdlength = rdlen; + rr->resrec.rdata->MaxRDLength = rdlen; + mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlen); + if (GetTTL) rr->resrec.rroriginalttl = ttl; + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + return rr; +} -Revision 1.143 2004/12/16 20:13:02 cheshire -<rdar://problem/3324626> Cache memory management improvements +mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain) +{ + domainlabel n; + domainname d, t; -Revision 1.142 2004/12/16 08:07:33 shersche -Fix compiler error (mixed declarations and code) on Windows + if (!MakeDomainLabelFromLiteralString(&n, name)) return -1; + if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1; + if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1; + if (!ConstructServiceName(srv, &n, &t, &d)) return -1; + return 0; +} + +mDNSlocal void send_all(dnssd_sock_t s, const char *ptr, int len) +{ + int n = send(s, ptr, len, 0); + // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a small write for us + // (four bytes for a typical error code return, 12 bytes for DNSServiceGetProperty(DaemonVersion)). + // If it does fail, we don't attempt to handle this failure, but we do log it so we know something is wrong. + if (n < len) + LogMsg("ERROR: send_all(%d) wrote %d of %d errno %d (%s)", + s, n, len, dnssd_errno, dnssd_strerror(dnssd_errno)); +} + +#if 0 +mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const domainname * const d, const DNameListElem * const doms) +{ + const DNameListElem *delem = mDNSNULL; + int bestDelta = -1; // the delta of the best match, lower is better + int dLabels = 0; + mDNSBool allow = mDNSfalse; + + if (SystemUID(request->uid)) return mDNStrue; + + dLabels = CountLabels(d); + for (delem = doms; delem; delem = delem->next) + { + if (delem->uid) + { + int delemLabels = CountLabels(&delem->name); + int delta = dLabels - delemLabels; + if ((bestDelta == -1 || delta <= bestDelta) && SameDomainName(&delem->name, SkipLeadingLabels(d, delta))) + { + bestDelta = delta; + allow = (allow || (delem->uid == request->uid)); + } + } + } -Revision 1.141 2004/12/16 01:56:21 cheshire -Improve DNSServiceEnumerateDomains syslog message + return bestDelta == -1 ? mDNStrue : allow; +} +#endif -Revision 1.140 2004/12/14 03:02:10 ksekar -<rdar://problem/3919016> Rare race condition can cause crash +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - external helpers +#endif -Revision 1.139 2004/12/13 21:18:45 ksekar -Include uDNS registrations in CountPeerRegistrations +mDNSlocal mDNSBool callExternalHelpers(mDNSInterfaceID InterfaceID, const domainname *const domain, DNSServiceFlags flags) +{ +#if APPLE_OSX_mDNSResponder -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 + if ( ((InterfaceID == mDNSInterface_Any) && (flags & (kDNSServiceFlagsIncludeP2P | kDNSServiceFlagsIncludeAWDL)) && IsLocalDomain(domain)) + || mDNSPlatformInterfaceIsD2D(InterfaceID)) + { + return mDNStrue; + } + else + return mDNSfalse; -Revision 1.137 2004/12/13 00:09:22 ksekar -<rdar://problem/3915805> mDNSResponder error when quitting iChat +#else + (void) InterfaceID; + (void) domain; + (void) flags; -Revision 1.136 2004/12/11 01:52:10 cheshire -<rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too + return mDNSfalse; +#endif // APPLE_OSX_mDNSResponder +} -Revision 1.135 2004/12/10 20:46:37 cheshire -Change LogOperation message to debugf +mDNSlocal void external_start_advertising_helper(service_instance *const instance) +{ + AuthRecord *st = instance->subtypes; + ExtraResourceRecord *e; + int i; -Revision 1.134 2004/12/10 13:19:37 cheshire -Add verbosedebugf() logging message in CountPeerRegistrations() + if (mDNSIPPortIsZero(instance->request->u.servicereg.port)) + { + LogInfo("external_start_advertising_helper: Not registering service with port number zero"); + return; + } -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 + if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!"); -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 + for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) + external_start_advertising_service(&st[i].resrec, instance->request->flags); -Revision 1.131 2004/12/10 02:09:25 cheshire -<rdar://problem/3898376> Modify default TTLs + external_start_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags); + external_start_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags); + external_start_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags); -Revision 1.130 2004/12/10 00:55:24 cheshire -Add full name and type to LogOperation messages for DNSServiceAddRecord/UpdateRecord/RemoveRecord + for (e = instance->srs.Extras; e; e = e->next) + external_start_advertising_service(&e->r.resrec, instance->request->flags); -Revision 1.129 2004/12/09 03:17:23 ksekar -<rdar://problem/3910435> DomainEnumeration interface index should be zero + instance->external_advertise = mDNStrue; +} -Revision 1.128 2004/12/07 21:26:05 ksekar -<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration +mDNSlocal void external_stop_advertising_helper(service_instance *const instance) +{ + AuthRecord *st = instance->subtypes; + ExtraResourceRecord *e; + int i; -Revision 1.127 2004/12/07 20:42:34 cheshire -Add explicit context parameter to mDNS_RemoveRecordFromService() + if (!instance->external_advertise) return; -Revision 1.126 2004/12/07 17:23:55 ksekar -Fixed LogOperation + LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service"); -Revision 1.125 2004/12/06 21:15:23 ksekar -<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) + external_stop_advertising_service(&st[i].resrec, instance->request->flags); -Revision 1.124 2004/11/30 02:19:14 cheshire -<rdar://problem/3827971> Raise maxfds.rlim_cur for mDNSResponder + external_stop_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags); + external_stop_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags); + external_stop_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags); -Revision 1.123 2004/11/29 23:50:57 cheshire -Checkin 1.122 not necessary + for (e = instance->srs.Extras; e; e = e->next) + external_stop_advertising_service(&e->r.resrec, instance->request->flags); -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 + instance->external_advertise = mDNSfalse; +} -Revision 1.121 2004/11/24 04:45:52 cheshire -Spelling mistake +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNSServiceRegister +#endif -Revision 1.120 2004/11/24 00:10:44 cheshire -<rdar://problem/3869241> For unicast operations, verify that service types are legal +mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext; + (void)m; // Unused -Revision 1.119 2004/11/23 23:54:17 ksekar -<rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures -can crash mDNSResponder + if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; } -Revision 1.118 2004/11/23 22:33:01 cheshire -<rdar://problem/3654910> Remove temporary workaround code for iChat + LogInfo(" FreeExtraRR %s", RRDisplayString(m, &rr->resrec)); -Revision 1.117 2004/11/23 20:23:10 ksekar -Fixed LogOperation that causes crash on connected service deregistrations + if (rr->resrec.rdata != &rr->rdatastorage) + freeL("Extra RData", rr->resrec.rdata); + freeL("ExtraResourceRecord/FreeExtraRR", extra); +} -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. +mDNSlocal void unlink_and_free_service_instance(service_instance *srv) +{ + ExtraResourceRecord *e = srv->srs.Extras, *tmp; -Revision 1.115 2004/11/13 00:12:53 ksekar -Fixed some LogOperation printf converstions for debug builds. + external_stop_advertising_helper(srv); -Revision 1.114 2004/11/12 18:25:45 shersche -Tidy up cross platform usleep code fragment. + // clear pointers from parent struct + if (srv->request) + { + service_instance **p = &srv->request->u.servicereg.instances; + while (*p) + { + if (*p == srv) { *p = (*p)->next; break; } + p = &(*p)->next; + } + } -Revision 1.113 2004/11/12 03:21:41 rpantos -rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + while (e) + { + e->r.RecordContext = e; + tmp = e; + e = e->next; + FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree); + } -Revision 1.112 2004/11/11 16:58:32 ksekar -Removed unused code (previously wrapped in #if 0) + if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage) + freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata); -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> + if (srv->subtypes) + { + freeL("ServiceSubTypes", srv->subtypes); + srv->subtypes = NULL; + } + if (srv->srs.AnonData) + { + freeL("Anonymous", (void *)srv->srs.AnonData); + srv->srs.AnonData = NULL; + } + freeL("service_instance", srv); +} -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_ +// 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; -Revision 1.109 2004/11/04 03:40:45 cheshire -More debugging messages + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !IdenticalSameNameRecord(&rr->resrec, r)) + count++; -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 + verbosedebugf("%d peer registrations for %##s", count, r->name->c); + return(count); +} -Revision 1.107 2004/11/02 19:39:23 ksekar -<rdar://problem/3862646> We no longer need to browse .Mac domains by default +mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port) +{ + int count = 0; + AuthRecord *rr; + for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && + mDNSSameIPPort(rr->resrec.rdata->u.srv.port, port) && + SameDomainName(rr->resrec.name, srv)) + count++; + return(count); +} + +mDNSlocal void SendServiceRemovalNotification(ServiceRecordSet *const srs) +{ + reply_state *rep; + service_instance *instance = srs->ServiceContext; + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, 0, mStatus_NoError) != mStatus_NoError) + LogMsg("%3d: SendServiceRemovalNotification: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + else { append_reply(instance->request, rep); instance->clientnotified = mDNSfalse; } +} -Revision 1.106 2004/11/02 02:12:21 cheshire -<rdar://problem/3839111> Remove unnecessary memory allocations +// service registration callback performs three duties - frees memory for deregistered services, +// handles name conflicts, and delivers completed registration information to the client +mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) +{ + mStatus err; + mDNSBool SuppressError = mDNSfalse; + service_instance *instance; + reply_state *rep; + (void)m; // Unused -Revision 1.105 2004/10/28 19:07:19 cheshire -Add some more debugging checks and improved LogOperation() messages + if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; } -Revision 1.104 2004/10/26 18:53:15 cheshire -Avoid unused variable warning + instance = srs->ServiceContext; + if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; } -Revision 1.103 2004/10/26 07:15:55 cheshire -Add file descriptor number to all LogOperation messages + // don't send errors up to client for wide-area, empty-string registrations + if (instance->request && + instance->request->u.servicereg.default_domain && + !instance->default_local) + SuppressError = mDNStrue; -Revision 1.102 2004/10/26 06:11:42 cheshire -Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed + if (mDNS_LoggingEnabled) + { + const char *const fmt = + (result == mStatus_NoError) ? "%s DNSServiceRegister(%##s, %u) REGISTERED" : + (result == mStatus_MemFree) ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED" : + (result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" : + "%s DNSServiceRegister(%##s, %u) %s %d"; + char prefix[16] = "---:"; + if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd); + LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), + SuppressError ? "suppressed error" : "CALLBACK", result); + } -Revision 1.101 2004/10/26 04:31:44 cheshire -Rename CountSubTypes() as ChopSubTypes() + if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; } -Revision 1.100 2004/10/26 01:17:48 cheshire -Use "#if 0" instead of commenting out code + if (result == mStatus_NoError) + { + if (instance->request->u.servicereg.allowremotequery) + { + ExtraResourceRecord *e; + srs->RR_ADV.AllowRemoteQuery = mDNStrue; + srs->RR_PTR.AllowRemoteQuery = mDNStrue; + srs->RR_SRV.AllowRemoteQuery = mDNStrue; + srs->RR_TXT.AllowRemoteQuery = mDNStrue; + for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue; + } -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 + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) + LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } -Revision 1.98 2004/10/14 01:59:33 cheshire -<rdar://problem/3839208> UDS resolves don't work for uDNS services + if (callExternalHelpers(instance->request->u.servicereg.InterfaceID, &instance->domain, instance->request->flags)) + { + LogInfo("regservice_callback: calling external_start_advertising_helper()"); + external_start_advertising_helper(instance); + } + if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0) + RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately + } + else if (result == mStatus_MemFree) + { + if (instance->request && instance->renameonmemfree) + { + external_stop_advertising_helper(instance); + instance->renameonmemfree = 0; + err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name); + if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err); + // error should never happen - safest to log and continue + } + else + unlink_and_free_service_instance(instance); + } + else if (result == mStatus_NameConflict) + { + if (instance->request->u.servicereg.autorename) + { + external_stop_advertising_helper(instance); + if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0) + { + // On conflict for an autoname service, rename and reregister *all* autoname services + IncrementLabelSuffix(&m->nicelabel, mDNStrue); + mDNS_ConfigChanged(m); // Will call back into udsserver_handle_configchange() + } + else // On conflict for a non-autoname service, rename and reregister just that one service + { + if (instance->clientnotified) SendServiceRemovalNotification(srs); + mDNS_RenameAndReregisterService(m, srs, mDNSNULL); + } + } + else + { + if (!SuppressError) + { + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) + LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } + } + unlink_and_free_service_instance(instance); + } + } + else // Not mStatus_NoError, mStatus_MemFree, or mStatus_NameConflict + { + if (!SuppressError) + { + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError) + LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); + else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } + } + } +} -Revision 1.97 2004/10/13 00:58:35 cheshire -<rdar://problem/3832738> Registering a proxy doesn't work +mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) +{ + (void)m; // Unused + if (!rr->RecordContext) // parent struct already freed by termination callback + { + if (result == mStatus_NoError) + LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr)); + else + { + if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result); + + // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination. + // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback + // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need + // to free the latest rdata for which the update_callback was never called with. + if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata); + freeL("AuthRecord/regrecord_callback", rr); + } + } + else + { + registered_record_entry *re = rr->RecordContext; + request_state *request = re->request; -Revision 1.96 2004/09/30 00:25:00 ksekar -<rdar://problem/3695802> Dynamically update default registration domains on config change + if (mDNS_LoggingEnabled) + { + char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED" : + (result == mStatus_MemFree) ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED" : + (result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" : + "%3d: DNSServiceRegisterRecord(%u %s) %d"; + LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result); + } -Revision 1.95 2004/09/26 23:20:36 ksekar -<rdar://problem/3813108> Allow default registrations in multiple wide-area domains + if (result != mStatus_MemFree) + { + int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType); + reply_state *reply = create_reply(reg_record_reply_op, len, request); + reply->mhdr->client_context = re->regrec_client_context; + reply->rhdr->flags = dnssd_htonl(0); + reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse)); + reply->rhdr->error = dnssd_htonl(result); + append_reply(request, reply); + } -Revision 1.94 2004/09/22 18:27:06 ksekar -<rdar://problem/3811427> allow DNSServiceAddRecord to pass zero to get -default record TTL + if (result) + { + // If this is a callback to a keepalive record, do not free it. + if (result == mStatus_BadStateErr) + { + LogInfo("regrecord_callback: Callback with error code mStatus_BadStateErr - not freeing the record."); + } + else + { + // unlink from list, free memory + registered_record_entry **ptr = &request->u.reg_recs; + while (*ptr && (*ptr) != re) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; } + *ptr = (*ptr)->next; + freeL("registered_record_entry AuthRecord regrecord_callback", re->rr); + freeL("registered_record_entry regrecord_callback", re); + } + } + else + { + if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!"); -Revision 1.93 2004/09/22 02:39:44 cheshire -<rdar://problem/3810757> Allow DNSServiceRegisterRecord to pass zero to get default record TTL + if (callExternalHelpers(re->origInterfaceID, &rr->namestorage, request->flags)) + { + LogInfo("regrecord_callback: calling external_start_advertising_service"); + external_start_advertising_service(&rr->resrec, request->flags); + re->external_advertise = mDNStrue; + } + } + } +} -Revision 1.92 2004/09/22 02:34:04 cheshire -Rename parameter "ttl" to "GetTTL" for clarity +// set_peer_pid() is called after mem is allocated for each new request in NewRequest() +// This accounts for 2 places (connect_callback, request_callback) +mDNSlocal void set_peer_pid(request_state *request) +{ +#ifdef LOCAL_PEERPID + pid_t p = (pid_t) -1; + socklen_t len = sizeof(p); -Revision 1.91 2004/09/22 02:25:43 cheshire -Fix spelling errors + request->pid_name[0] = '\0'; + request->process_id = -1; -Revision 1.90 2004/09/21 23:40:12 ksekar -<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure + if (request->sd < 0) + return; + // to extract the pid value + if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEERPID, &p, &len) != 0) + return; + // to extract the process name from the pid value + if (proc_pidinfo(p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) + return; + mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm); + request->process_id = p; +#else // !LOCAL_PEERPID + request->pid_name[0] = '\0'; + request->process_id = -1; -Revision 1.89 2004/09/21 23:29:51 cheshire -<rdar://problem/3680045> DNSServiceResolve should delay sending packets + if (request->sd < 0) + return; + LogInfo("set_peer_pid: Not Supported on this version of OS"); +#endif // LOCAL_PEERPID +} -Revision 1.88 2004/09/21 23:12:46 cheshire -Reorder initialization of question fields to match structure order +mDNSlocal void connection_termination(request_state *request) +{ + // When terminating a shared connection, we need to scan the all_requests list + // and terminate any subbordinate operations sharing this file descriptor + request_state **req = &all_requests; -Revision 1.87 2004/09/21 22:18:33 cheshire -In SIGINFO output, display a '-' next to records that have the Unique bit set + LogOperation("%3d: DNSServiceCreateConnection STOP PID[%d](%s)", request->sd, request->process_id, request->pid_name); -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 + while (*req) + { + if ((*req)->primary == request) + { + // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() + request_state *tmp = *req; + if (tmp->primary == tmp) LogMsg("connection_termination ERROR (*req)->primary == *req for %p %d", tmp, tmp->sd); + if (tmp->replies) LogMsg("connection_termination ERROR How can subordinate req %p %d have replies queued?", tmp, tmp->sd); + abort_request(tmp); + *req = tmp->next; + freeL("request_state/connection_termination", tmp); + } + else + req = &(*req)->next; + } -Revision 1.85 2004/09/18 01:11:58 ksekar -<rdar://problem/3806734> Add a user's default domain to empty-string browse list + while (request->u.reg_recs) + { + registered_record_entry *ptr = request->u.reg_recs; + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP PID[%d](%s)", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), request->process_id, request->pid_name); + request->u.reg_recs = request->u.reg_recs->next; + ptr->rr->RecordContext = NULL; + if (ptr->external_advertise) + { + ptr->external_advertise = mDNSfalse; + external_stop_advertising_service(&ptr->rr->resrec, request->flags); + } + LogMcastS(&mDNSStorage, ptr->rr, request, reg_stop); + mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us + freeL("registered_record_entry/connection_termination", ptr); + } +} -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. +mDNSlocal void handle_cancel_request(request_state *request) +{ + request_state **req = &all_requests; + LogOperation("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]); + while (*req) + { + if ((*req)->primary == request && + (*req)->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] && + (*req)->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) + { + // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() + request_state *tmp = *req; + abort_request(tmp); + *req = tmp->next; + freeL("request_state/handle_cancel_request", tmp); + } + else + req = &(*req)->next; + } +} -Revision 1.83 2004/09/16 23:26:33 cheshire -Move version check inside preceeding "if" that checks we have a complete header +mDNSlocal mStatus handle_regrecord_request(request_state *request) +{ + mStatus err = mStatus_BadParamErr; + AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1); + if (rr) + { + registered_record_entry *re; + // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit + // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari. + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) && + rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA || + rr->resrec.rrtype == kDNSType_CNAME)) + { + freeL("AuthRecord/handle_regrecord_request", rr); + return (mStatus_BadParamErr); + } + // allocate registration entry, link into list + re = mallocL("registered_record_entry", sizeof(registered_record_entry)); + if (!re) + FatalError("ERROR: malloc"); + re->key = request->hdr.reg_index; + re->rr = rr; + re->regrec_client_context = request->hdr.client_context; + re->request = request; + re->external_advertise = mDNSfalse; + rr->RecordContext = re; + rr->RecordCallback = regrecord_callback; + + re->origInterfaceID = rr->resrec.InterfaceID; + if (rr->resrec.InterfaceID == mDNSInterface_P2P) + rr->resrec.InterfaceID = mDNSInterface_Any; +#if 0 + if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError); +#endif + if (rr->resrec.rroriginalttl == 0) + rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); -Revision 1.82 2004/09/16 23:14:25 cheshire -Changes for Windows compatibility + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), + request->process_id, request->pid_name); -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 + err = mDNS_Register(&mDNSStorage, rr); + if (err) + { + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err); + freeL("registered_record_entry", re); + freeL("registered_record_entry/AuthRecord", rr); + } + else + { + LogMcastS(&mDNSStorage, rr, request, reg_start); + re->next = request->u.reg_recs; + request->u.reg_recs = re; + } + } + return(err); +} -Revision 1.80 2004/09/16 01:58:23 cheshire -Fix compiler warnings +mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m); -Revision 1.79 2004/09/16 00:24:49 cheshire -<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() +mDNSlocal void regservice_termination_callback(request_state *request) +{ + if (!request) + { + LogMsg("regservice_termination_callback context is NULL"); + return; + } + while (request->u.servicereg.instances) + { + service_instance *p = request->u.servicereg.instances; + request->u.servicereg.instances = request->u.servicereg.instances->next; + // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p) + LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)", request->sd, p->srs.RR_SRV.resrec.name->c, + mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), request->process_id, request->pid_name); + + external_stop_advertising_helper(p); + + // Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance + // We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing + // request->u.servicereg.instances as we work our way through the list, implicitly cutting one element at a time + // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance + // because by then we might have already freed p + p->request = NULL; + LogMcastS(&mDNSStorage, &p->srs.RR_SRV, request, reg_stop); + if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) + { + unlink_and_free_service_instance(p); + // Don't touch service_instance *p after this -- it's likely to have been freed already + } + } + if (request->u.servicereg.txtdata) + { + freeL("service_info txtdata", request->u.servicereg.txtdata); + request->u.servicereg.txtdata = NULL; + } + if (request->u.servicereg.autoname) + { + // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations + request->u.servicereg.autoname = mDNSfalse; + UpdateDeviceInfoRecord(&mDNSStorage); + } +} -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 +mDNSlocal request_state *LocateSubordinateRequest(request_state *request) +{ + request_state *req; + for (req = all_requests; req; req = req->next) + if (req->primary == request && + req->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] && + req->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) return(req); + return(request); +} + +mDNSlocal mStatus add_record_to_service(request_state *request, service_instance *instance, mDNSu16 rrtype, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl) +{ + ServiceRecordSet *srs = &instance->srs; + mStatus result; + mDNSu32 coreFlags = 0; // translate to corresponding mDNSCore flag definitions + int size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); + if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } -Revision 1.77 2004/09/15 00:19:18 cheshire -<rdar://problem/3785823> read_rr_from_ipc_msg should use mDNS_SetupResourceRecord() + mDNSPlatformMemZero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd + extra->r.resrec.rrtype = rrtype; + extra->r.rdatastorage.MaxRDLength = (mDNSu16) size; + extra->r.resrec.rdlength = rdlen; + mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen); + // use InterfaceID value from DNSServiceRegister() call that created the original service + extra->r.resrec.InterfaceID = request->u.servicereg.InterfaceID; + + if (request->flags & kDNSServiceFlagsIncludeP2P) + coreFlags |= coreFlagIncludeP2P; + if (request->flags & kDNSServiceFlagsIncludeAWDL) + coreFlags |= coreFlagIncludeAWDL; + + result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl, coreFlags); + if (result) + { + freeL("ExtraResourceRecord/add_record_to_service", extra); + return result; + } + LogMcastS(&mDNSStorage, &srs->RR_PTR, request, reg_start); + + extra->ClientID = request->hdr.reg_index; + if ( instance->external_advertise + && callExternalHelpers(request->u.servicereg.InterfaceID, &instance->domain, request->flags)) + { + LogInfo("add_record_to_service: calling external_start_advertising_service"); + external_start_advertising_service(&extra->r.resrec, request->flags); + } + return result; +} -Revision 1.76 2004/09/02 06:39:52 cheshire -Minor textual cleanup for clarity +mDNSlocal mStatus handle_add_request(request_state *request) +{ + service_instance *i; + mStatus result = mStatus_UnknownErr; + DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); + mDNSu16 rrtype = get_uint16(&request->msgptr, request->msgend); + mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); + const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend); + if (!ttl) ttl = DefaultTTLforRRType(rrtype); + (void)flags; // Unused -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 + if (!request->msgptr) { LogMsg("%3d: DNSServiceAddRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } -Revision 1.74 2004/08/25 02:32:47 cheshire -Minor cleanup: replace "®type[0]" with "regtype" + // If this is a shared connection, check if the operation actually applies to a subordinate request_state object + if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); -Revision 1.73 2004/08/25 02:30:40 cheshire -<rdar://problem/3588761> Current method of doing subtypes causes name collisions + if (request->terminate != regservice_termination_callback) + { LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } -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 + // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug + // in the application. See radar://9165807. + if (mDNSIPPortIsZero(request->u.servicereg.port)) + { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); } -Revision 1.71 2004/08/11 04:21:21 rpantos -Fix Windows build. + LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags, + (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen); -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 + for (i = request->u.servicereg.instances; i; i = i->next) + { + result = add_record_to_service(request, i, rrtype, rdlen, rdata, ttl); + if (result && i->default_local) break; + else result = mStatus_NoError; // suppress non-local default errors + } -Revision 1.69 2004/08/10 16:14:48 cheshire -Fix debug builds (oops) + return(result); +} -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 +mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd, mDNSu16 oldrdlen) +{ + mDNSBool external_advertise = (rr->UpdateContext) ? *((mDNSBool *)rr->UpdateContext) : mDNSfalse; + (void)m; // Unused -Revision 1.67 2004/07/27 07:14:16 shersche -make error socket non-blocking after call to connect() + // There are three cases. + // + // 1. We have updated the primary TXT record of the service + // 2. We have updated the TXT record that was added to the service using DNSServiceAddRecord + // 3. We have updated the TXT record that was registered using DNSServiceRegisterRecord + // + // external_advertise is set if we have advertised at least once during the initial addition + // of the record in all of the three cases above. We should have checked for InterfaceID/LocalDomain + // checks during the first time and hence we don't do any checks here + if (external_advertise) + { + ResourceRecord ext = rr->resrec; + DNSServiceFlags flags = 0; + + // Since we don't have a copy of the flags value used when the record was registered, + // we'll have to derive it from the ARType field. + if (rr->ARType == AuthRecordAnyIncludeP2P) + flags |= kDNSServiceFlagsIncludeP2P; + else if (rr->ARType == AuthRecordAnyIncludeAWDL) + flags |= kDNSServiceFlagsIncludeAWDL; + + if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit; + SetNewRData(&ext, oldrd, oldrdlen); + external_stop_advertising_service(&ext, flags); + LogInfo("update_callback: calling external_start_advertising_service"); + external_start_advertising_service(&rr->resrec, flags); + } +exit: + if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd); +} -Revision 1.66 2004/07/13 21:24:25 rpantos -Fix for <rdar://problem/3701120>. +mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl, const mDNSBool *const external_advertise) +{ + mStatus result; + const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize); + if (!newrd) FatalError("ERROR: malloc"); + newrd->MaxRDLength = (mDNSu16) rdsize; + mDNSPlatformMemCopy(&newrd->u, rdata, rdlen); -Revision 1.65 2004/06/26 03:17:14 shersche -implement cross-platform strerror function + // 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; } -Submitted by: herscher + if (external_advertise) rr->UpdateContext = (void *)external_advertise; -Revision 1.64 2004/06/25 00:26:27 rpantos -Changes to fix the Posix build on Solaris. + result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback); + if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); } + return result; +} -Revision 1.63 2004/06/24 03:43:44 rpantos -Fix previous checkin so it builds on Windows. +mDNSlocal mStatus handle_update_request(request_state *request) +{ + const ipc_msg_hdr *const hdr = &request->hdr; + mStatus result = mStatus_BadReferenceErr; + service_instance *i; + AuthRecord *rr = NULL; -Revision 1.62 2004/06/24 00:57:08 ksekar -Replaced code acccidentally removed in checkin 1.59. + // get the message data + DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend); // flags unused + mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); + const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); + mDNSu32 ttl = get_uint32(&request->msgptr, request->msgend); + (void)flags; // Unused -Revision 1.61 2004/06/19 00:09:39 cheshire -Remove unused strsep() implementation + if (!request->msgptr) { LogMsg("%3d: DNSServiceUpdateRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } -Revision 1.60 2004/06/18 19:10:00 cheshire -<rdar://problem/3588761> Current method of doing subtypes causes name collisions + // If this is a shared connection, check if the operation actually applies to a subordinate request_state object + if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); -Revision 1.59 2004/06/18 05:10:31 rpantos -Changes to allow code to be used on Windows + if (request->terminate == connection_termination) + { + // update an individually registered record + registered_record_entry *reptr; + for (reptr = request->u.reg_recs; reptr; reptr = reptr->next) + { + if (reptr->key == hdr->reg_index) + { + result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise); + LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", + request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : "<NONE>"); + goto end; + } + } + result = mStatus_BadReferenceErr; + goto end; + } -Revision 1.58 2004/06/15 03:54:08 cheshire -Include mDNS_TimeNow(&mDNSStorage) in SIGINFO output + if (request->terminate != regservice_termination_callback) + { LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } -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. + // For a service registered with zero port, only SRV record is initialized. Don't allow any updates. + if (mDNSIPPortIsZero(request->u.servicereg.port)) + { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); } -Revision 1.56 2004/06/12 01:35:47 cheshire -Changes for Windows compatibility + // update the saved off TXT data for the service + if (hdr->reg_index == TXT_RECORD_INDEX) + { + if (request->u.servicereg.txtdata) + { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; } + if (rdlen > 0) + { + request->u.servicereg.txtdata = mallocL("service_info txtdata", rdlen); + if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_update_request - malloc"); + mDNSPlatformMemCopy(request->u.servicereg.txtdata, rdata, rdlen); + } + request->u.servicereg.txtlen = rdlen; + } -Revision 1.55 2004/06/05 00:04:27 cheshire -<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration + // update a record from a service record set + for (i = request->u.servicereg.instances; i; i = i->next) + { + if (hdr->reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT; + else + { + ExtraResourceRecord *e; + for (e = i->srs.Extras; e; e = e->next) + if (e->ClientID == hdr->reg_index) { rr = &e->r; break; } + } -Revision 1.54 2004/06/01 22:22:52 ksekar -<rdar://problem/3668635>: wide-area default registrations should be in -.local too + if (!rr) { result = mStatus_BadReferenceErr; goto end; } + result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise); + if (result && i->default_local) goto end; + else result = mStatus_NoError; // suppress non-local default errors + } -Revision 1.53 2004/05/28 23:42:37 ksekar -<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) +end: + if (request->terminate == regservice_termination_callback) + LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", request->sd, + (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, + rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>"); -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 + return(result); +} -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 +// remove a resource record registered via DNSServiceRegisterRecord() +mDNSlocal mStatus remove_record(request_state *request) +{ + mStatus err = mStatus_UnknownErr; + registered_record_entry *e, **ptr = &request->u.reg_recs; -Revision 1.50 2004/05/14 16:39:47 ksekar -Browse for iChat locally for now. + while (*ptr && (*ptr)->key != request->hdr.reg_index) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("%3d: DNSServiceRemoveRecord(%u) not found", request->sd, request->hdr.reg_index); return mStatus_BadReferenceErr; } + e = *ptr; + *ptr = e->next; // unlink -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. + LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, e->key, RRDisplayString(&mDNSStorage, &e->rr->resrec)); + e->rr->RecordContext = NULL; + if (e->external_advertise) + { + external_stop_advertising_service(&e->rr->resrec, request->flags); + e->external_advertise = mDNSfalse; + } + LogMcastS(&mDNSStorage, e->rr, request, reg_stop); + err = mDNS_Deregister(&mDNSStorage, e->rr); // Will free e->rr for us; we're responsible for freeing e + if (err) + { + LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err); + freeL("registered_record_entry AuthRecord remove_record", e->rr); + } + freeL("registered_record_entry remove_record", e); + return err; +} -Revision 1.48 2004/05/13 04:13:19 ksekar -Updated SIGINFO handler for multi-domain browses +mDNSlocal mStatus remove_extra(const request_state *const request, service_instance *const serv, mDNSu16 *const rrtype) +{ + mStatus err = mStatus_BadReferenceErr; + ExtraResourceRecord *ptr; -Revision 1.47 2004/05/12 22:04:01 ksekar -Implemented multi-domain browsing by default for uds_daemon. + for (ptr = serv->srs.Extras; ptr; ptr = ptr->next) + { + if (ptr->ClientID == request->hdr.reg_index) // found match + { + *rrtype = ptr->r.resrec.rrtype; + if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec, request->flags); + err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr); + break; + } + } + return err; +} -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. +mDNSlocal mStatus handle_removerecord_request(request_state *request) +{ + mStatus err = mStatus_BadReferenceErr; + get_flags(&request->msgptr, request->msgend); // flags unused -Revision 1.45 2004/03/12 08:49:28 cheshire -#include <sys/socket.h> + if (!request->msgptr) { LogMsg("%3d: DNSServiceRemoveRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } -Revision 1.44 2004/02/25 01:25:27 ksekar -<rdar://problem/3569212>: DNSServiceRegisterRecord flags not error-checked + // If this is a shared connection, check if the operation actually applies to a subordinate request_state object + if (request->terminate == connection_termination) request = LocateSubordinateRequest(request); -Revision 1.43 2004/02/24 01:46:40 cheshire -Manually reinstate lost checkin 1.36 + if (request->terminate == connection_termination) + err = remove_record(request); // remove individually registered record + else if (request->terminate != regservice_termination_callback) + { LogMsg("%3d: DNSServiceRemoveRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); } + else + { + service_instance *i; + mDNSu16 rrtype = 0; + LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s)", request->sd, + (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, + rrtype ? DNSTypeName(rrtype) : "<NONE>"); + for (i = request->u.servicereg.instances; i; i = i->next) + { + err = remove_extra(request, i, &rrtype); + if (err && i->default_local) break; + else err = mStatus_NoError; // suppress non-local default errors + } + } -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 + return(err); +} -Revision 1.41 2004/02/03 18:59:02 cheshire -Change "char *domain" parameter for format_enumeration_reply to "const char *domain" +// 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, char **AnonData) +{ + while (*p) + { + if (p[0] == '\\' && p[1]) + { + p += 2; + } + else if (p[0] == ',' && p[1]) + { + *p++ = 0; + return(p); + } + else if (p[0] == ':' && p[1]) + { + *p++ = 0; + *AnonData = p; + } + else + { + p++; + } + } + return(p); +} -Revision 1.40 2004/01/28 03:41:00 cheshire -<rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries +// 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); +} -Revision 1.39 2004/01/25 00:03:21 cheshire -Change to use mDNSVal16() instead of private PORT_AS_NUM() macro +// Returns -1 if illegal subtype found +mDNSexport mDNSs32 ChopSubTypes(char *regtype, char **AnonData) +{ + mDNSs32 NumSubTypes = 0; + char *stp = FindFirstSubType(regtype, AnonData); + 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, char **AnonData) +{ + AuthRecord *st = mDNSNULL; + // + // "p" is pointing at the regtype e.g., _http._tcp followed by ":<AnonData>" indicated + // by AnonData being non-NULL which is in turn follwed by ",<SubTypes>" indicated by + // NumSubTypes being non-zero. We need to skip the initial regtype to get to the actual + // data that we want. When we come here, ChopSubTypes has null terminated like this e.g., + // + // _http._tcp<NULL><AnonData><NULL><SubType1><NULL><SubType2><NULL> etc. + // + // 1. If we have Anonymous data and subtypes, skip the regtype (e.g., "_http._tcp") + // to get the AnonData and then skip the AnonData to get to the SubType. + // + // 2. If we have only SubTypes, skip the regtype to get to the SubType data. + // + // 3. If we have only AnonData, skip the regtype to get to the AnonData. + // + // 4. If we don't have AnonData or NumStypes, it is a noop. + // + if (AnonData) + { + int len; -Revision 1.38 2004/01/19 19:51:46 cheshire -Fix compiler error (mixed declarations and code) on some versions of Linux + // Skip the regtype + while (*p) p++; + p++; -Revision 1.37 2003/12/08 21:11:42 rpantos -Changes necessary to support mDNSResponder on Linux. + len = strlen(p) + 1; + *AnonData = mallocL("Anonymous", len); + if (!(*AnonData)) + { + return (mDNSNULL); + } + mDNSPlatformMemCopy(*AnonData, p, len); + } + if (NumSubTypes) + { + mDNSs32 i; + st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); + if (!st) return(mDNSNULL); + for (i = 0; i < NumSubTypes; i++) + { + mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL); + // First time through we skip the regtype or AnonData. Subsequently, the + // previous subtype. + while (*p) p++; + p++; + if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p)) + { + freeL("ServiceSubTypes", st); + if (*AnonData) + freeL("AnonymousData", *AnonData); + return(mDNSNULL); + } + } + } + // If NumSubTypes is zero and AnonData is non-NULL, we still return NULL but AnonData has been + // initialized. The caller knows how to handle this. + return(st); +} -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. +mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain) +{ + service_instance **ptr, *instance; + const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0; + const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain); + mStatus result; + mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID; + mDNSu32 coreFlags = 0; + + if (request->flags & kDNSServiceFlagsIncludeP2P) + coreFlags |= coreFlagIncludeP2P; + if (request->flags & kDNSServiceFlagsIncludeAWDL) + coreFlags |= coreFlagIncludeAWDL; + + // Client guarantees that record names are unique, so we can skip sending out initial + // probe messages. Standard name conflict resolution is still done if a conflict is discovered. + if (request->flags & kDNSServiceFlagsKnownUnique) + coreFlags |= coreFlagKnownUnique; + + if (request->flags & kDNSServiceFlagsWakeOnlyService) + coreFlags |= coreFlagWakeOnly; + + // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS) + // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast + // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface. + // (Specifying an interface with an apparently wide-area domain (i.e. something other than "local") + // currently forces the registration to use mDNS multicast despite the apparently wide-area domain.) + if (request->u.servicereg.default_domain && !DomainIsLocal) interfaceID = mDNSInterface_Any; + + for (ptr = &request->u.servicereg.instances; *ptr; ptr = &(*ptr)->next) + { + if (SameDomainName(&(*ptr)->domain, domain)) + { + LogMsg("register_service_instance: domain %##s already registered for %#s.%##s", + domain->c, &request->u.servicereg.name, &request->u.servicereg.type); + return mStatus_AlreadyRegistered; + } + } -Revision 1.35 2003/12/03 19:10:22 ksekar -<rdar://problem/3498644>: malloc'd data not zero'd + instance = mallocL("service_instance", sizeof(*instance) + extra_size); + if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } -Revision 1.34 2003/12/03 02:00:01 ksekar -<rdar://problem/3498644>: malloc'd data not zero'd + instance->next = mDNSNULL; + instance->request = request; + instance->renameonmemfree = 0; + instance->clientnotified = mDNSfalse; + instance->default_local = (request->u.servicereg.default_domain && DomainIsLocal); + instance->external_advertise = mDNSfalse; + AssignDomainName(&instance->domain, domain); -Revision 1.33 2003/11/22 01:18:46 ksekar -<rdar://problem/3486646>: config change handler not called for dns-sd services + instance->srs.AnonData = mDNSNULL; + if (!request->u.servicereg.AnonData) + { + instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, mDNSNULL); + } + else + { + char *AnonData = mDNSNULL; + instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, &AnonData); + if (AnonData) + instance->srs.AnonData = (const mDNSu8 *)AnonData; + } -Revision 1.32 2003/11/20 21:46:12 ksekar -<rdar://problem/3486635>: leak: DNSServiceRegisterRecord + if (request->u.servicereg.num_subtypes && !instance->subtypes) + { + unlink_and_free_service_instance(instance); + instance = NULL; + FatalError("ERROR: malloc"); + } -Revision 1.31 2003/11/20 20:33:05 ksekar -<rdar://problem/3486635>: leak: DNSServiceRegisterRecord + result = mDNS_RegisterService(&mDNSStorage, &instance->srs, + &request->u.servicereg.name, &request->u.servicereg.type, domain, + request->u.servicereg.host.c[0] ? &request->u.servicereg.host : NULL, + request->u.servicereg.port, + request->u.servicereg.txtdata, request->u.servicereg.txtlen, + instance->subtypes, request->u.servicereg.num_subtypes, + interfaceID, regservice_callback, instance, coreFlags); -Revision 1.30 2003/11/20 02:10:55 ksekar -<rdar://problem/3486643>: cleanup DNSServiceAdd/RemoveRecord + if (!result) + { + *ptr = instance; // Append this to the end of our request->u.servicereg.instances list + LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", instance->request->sd, + instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port)); + LogMcastS(&mDNSStorage, &instance->srs.RR_SRV, request, reg_start); + } + else + { + LogMsg("register_service_instance %#s.%##s%##s error %d", + &request->u.servicereg.name, &request->u.servicereg.type, domain->c, result); + unlink_and_free_service_instance(instance); + } -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. + return result; +} -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 +mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d, const mDNSBool add) +{ + request_state *request; -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 +#if APPLE_OSX_mDNSResponder + machserver_automatic_registration_domain_changed(&d->name, add); +#endif // APPLE_OSX_mDNSResponder -Revision 1.26 2003/10/23 17:51:04 ksekar -<rdar://problem/3335216>: handle blocked clients more efficiently -Changed gettimeofday() to mDNS_TimeNow(&mDNSStorage); + LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->name.c); + for (request = all_requests; request; request = request->next) + { + if (request->terminate != regservice_termination_callback) continue; + if (!request->u.servicereg.default_domain) continue; + if (!d->uid || SystemUID(request->uid) || request->uid == d->uid) + { + service_instance **ptr = &request->u.servicereg.instances; + while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next; + if (add) + { + // If we don't already have this domain in our list for this registration, add it now + if (!*ptr) register_service_instance(request, &d->name); + else debugf("udsserver_default_reg_domain_changed %##s already in list, not re-adding", &d->name); + } + else + { + // Normally we should not fail to find the specified instance + // One case where this can happen is if a uDNS update fails for some reason, + // and regservice_callback then calls unlink_and_free_service_instance and disposes of that instance. + if (!*ptr) + LogMsg("udsserver_default_reg_domain_changed domain %##s not found for service %#s type %s", + &d->name, request->u.servicereg.name.c, request->u.servicereg.type_as_string); + else + { + DNameListElem *p; + for (p = AutoRegistrationDomains; p; p=p->next) + if (!p->uid || SystemUID(request->uid) || request->uid == p->uid) + if (SameDomainName(&d->name, &p->name)) break; + if (p) debugf("udsserver_default_reg_domain_changed %##s still in list, not removing", &d->name); + else + { + mStatus err; + service_instance *si = *ptr; + *ptr = si->next; + if (si->clientnotified) SendServiceRemovalNotification(&si->srs); // Do this *before* clearing si->request backpointer + // Now that we've cut this service_instance from the list, we MUST clear the si->request backpointer. + // Otherwise what can happen is this: While our mDNS_DeregisterService is in the + // process of completing asynchronously, the client cancels the entire operation, so + // regservice_termination_callback then runs through the whole list deregistering each + // instance, clearing the backpointers, and then disposing the parent request_state object. + // However, because this service_instance isn't in the list any more, regservice_termination_callback + // has no way to find it and clear its backpointer, and then when our mDNS_DeregisterService finally + // completes later with a mStatus_MemFree message, it calls unlink_and_free_service_instance() with + // a service_instance with a stale si->request backpointer pointing to memory that's already been freed. + si->request = NULL; + err = mDNS_DeregisterService(&mDNSStorage, &si->srs); + if (err) { LogMsg("udsserver_default_reg_domain_changed err %d", err); unlink_and_free_service_instance(si); } + } + } + } + } + } +} -Revision 1.25 2003/10/22 23:37:49 ksekar -<rdar://problem/3459141>: crash/hang in abort_client +// Don't allow normal and anonymous registration to coexist. +mDNSlocal mDNSBool CheckForMixedRegistrations(domainname *regtype, domainname *domain, mDNSBool AnonData) +{ + request_state *request; -Revision 1.24 2003/10/21 20:59:40 ksekar -<rdar://problem/3335216>: handle blocked clients more efficiently + // We only care about local domains where the anonymous extension is + // implemented. + if (!SameDomainName(domain, (const domainname *) "\x5" "local")) + { + return mDNStrue; + } -Revision 1.23 2003/09/23 02:12:43 cheshire -Also include port number in list of services registered via new UDS API + for (request = all_requests; request; request = request->next) + { + service_instance *ptr; -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. + if (request->terminate != regservice_termination_callback) continue; + for (ptr = request->u.servicereg.instances; ptr ; ptr = ptr->next) + { + if (!SameDomainName(&ptr->domain, (const domainname *)"\x5" "local") || + !SameDomainName(&request->u.servicereg.type, regtype)) + { + continue; + } -Revision 1.21 2003/08/19 05:39:43 cheshire -<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord + // If we are about to register a anonymous registraion, we dont't want to + // allow the regular ones and vice versa. + if (AnonData) + { + if (!ptr->srs.AnonData) + { + LogMsg("CheckForMixedRegistrations: Normal registration already exists for %##s", regtype->c); + return mDNSfalse; + } + } + else + { + // Allow multiple regular registrations + if (ptr->srs.AnonData) + { + LogMsg("CheckForMixedRegistrations: Anonymous registration already exists for %##s", regtype->c); + return mDNSfalse; + } + } + } + } + return mDNStrue; +} + +// Returns true if the interfaceIndex value matches one of the pre-defined +// special values listed in the switch statement below. +mDNSlocal mDNSBool PreDefinedInterfaceIndex(mDNSu32 interfaceIndex) +{ + switch(interfaceIndex) + { + case kDNSServiceInterfaceIndexAny: + case kDNSServiceInterfaceIndexLocalOnly: + case kDNSServiceInterfaceIndexUnicast: + case kDNSServiceInterfaceIndexP2P: + return mDNStrue; + break; + default: + return mDNSfalse; + } +} -Revision 1.20 2003/08/16 03:39:01 cheshire -<rdar://problem/3338440> InterfaceID -1 indicates "local only" +mDNSlocal mStatus handle_regservice_request(request_state *request) +{ + char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes + char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME]; + char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; + domainname d, srv; + mStatus err; + char *AnonData = mDNSNULL; -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. + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID; -Revision 1.18 2003/08/15 00:38:00 ksekar -<rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client + // Map kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny with the + // kDNSServiceFlagsIncludeP2P flag set. + if (interfaceIndex == kDNSServiceInterfaceIndexP2P) + { + LogOperation("handle_regservice_request: mapping kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny + kDNSServiceFlagsIncludeP2P"); + flags |= kDNSServiceFlagsIncludeP2P; + interfaceIndex = kDNSServiceInterfaceIndexAny; + } -Revision 1.17 2003/08/14 02:18:21 cheshire -<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); -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. + // The registration is scoped to a specific interface index, but the + // interface is not currently in our list. + if (interfaceIndex && !InterfaceID) + { + // If it's one of the specially defined inteface index values, just return an error. + if (PreDefinedInterfaceIndex(interfaceIndex)) + { + LogMsg("ERROR: handle_regservice_request: bad interfaceIndex %d", interfaceIndex); + return(mStatus_BadParamErr); + } -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. + // Otherwise, use the specified interface index value and the registration will + // be applied to that interface when it comes up. + InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; + LogInfo("handle_regservice_request: registration pending for interface index %d", interfaceIndex); + } -Revision 1.14 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 + if (get_string(&request->msgptr, request->msgend, name, sizeof(name)) < 0 || + get_string(&request->msgptr, request->msgend, type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0) + { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } - */ + request->flags = flags; + request->u.servicereg.InterfaceID = InterfaceID; + request->u.servicereg.instances = NULL; + request->u.servicereg.txtlen = 0; + request->u.servicereg.txtdata = NULL; + mDNSPlatformStrCopy(request->u.servicereg.type_as_string, type_as_string); -#pragma ident "%Z%%M% %I% %E% SMI" + if (request->msgptr + 2 > request->msgend) request->msgptr = NULL; + else + { + request->u.servicereg.port.b[0] = *request->msgptr++; + request->u.servicereg.port.b[1] = *request->msgptr++; + } -#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 + request->u.servicereg.txtlen = get_uint16(&request->msgptr, request->msgend); + if (request->u.servicereg.txtlen) + { + request->u.servicereg.txtdata = mallocL("service_info txtdata", request->u.servicereg.txtlen); + if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc"); + mDNSPlatformMemCopy(request->u.servicereg.txtdata, get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen), request->u.servicereg.txtlen); + } -#include <stdlib.h> -#include <stdio.h> -#include "mDNSEmbeddedAPI.h" -#include "DNSCommon.h" -#include "uds_daemon.h" -#include "dns_sd.h" -#include "dnssd_ipc.h" + if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } -// 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__ + // Check for sub-types after the service type + request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string, &AnonData); // Note: Modifies regtype string to remove trailing subtypes + if (request->u.servicereg.num_subtypes < 0) + { + LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); + return(mStatus_BadParamErr); + } + if (AnonData) + { + int AnonDataLen = strlen(AnonData); + if (AnonDataLen > MAX_ANONYMOUS_DATA) + { + LogMsg("ERROR: handle_regservice_request: AnonDataLen %d", AnonDataLen); + return(mStatus_BadParamErr); + } + request->u.servicereg.AnonData = mDNStrue; + } + else + { + request->u.servicereg.AnonData = mDNSfalse; + } -#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS) -extern mStatus dDNS_RegisterSearchDomains( mDNS * const m ); -#endif + // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic + if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string)) + { LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); } -// Types and Data Structures -// ---------------------------------------------------------------------- + if (!name[0]) + { + request->u.servicereg.name = mDNSStorage.nicelabel; + request->u.servicereg.autoname = mDNStrue; + } + else + { + // If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel + if ((flags & kDNSServiceFlagsNoAutoRename) == 0) + { + int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL); + name[newlen] = 0; + } + if (!MakeDomainLabelFromLiteralString(&request->u.servicereg.name, name)) + { LogMsg("ERROR: handle_regservice_request - name bad %s", name); return(mStatus_BadParamErr); } + request->u.servicereg.autoname = mDNSfalse; + } -typedef enum + if (*domain) { - t_uninitialized, - t_morecoming, - t_complete, - t_error, - t_terminated - } transfer_state; + request->u.servicereg.default_domain = mDNSfalse; + if (!MakeDomainNameFromDNSNameString(&d, domain)) + { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); return(mStatus_BadParamErr); } + } + else + { + request->u.servicereg.default_domain = mDNStrue; + MakeDomainNameFromDNSNameString(&d, "local."); + } -typedef void (*req_termination_fn)(void *); + // We don't allow the anonymous and the regular ones to coexist + if (!CheckForMixedRegistrations(&request->u.servicereg.type, &d, request->u.servicereg.AnonData)) + { + return(mStatus_BadParamErr); + } -typedef struct registered_record_entry + if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d)) { - uint32_t key; - AuthRecord *rr; - struct registered_record_entry *next; - client_context_t client_context; - struct request_state *rstate; - } registered_record_entry; + LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", + request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); return(mStatus_BadParamErr); + } -// 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 + if (!MakeDomainNameFromDNSNameString(&request->u.servicereg.host, host)) + { LogMsg("ERROR: handle_regservice_request - host bad %s", host); return(mStatus_BadParamErr); } + request->u.servicereg.autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0; + request->u.servicereg.allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0; + + // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with + // a port number of zero. When two instances of the protected client are allowed to run on one + // machine, we don't want to see misleading "Bogus client" messages in syslog and the console. + if (!mDNSIPPortIsZero(request->u.servicereg.port)) { - 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; + int count = CountExistingRegistrations(&srv, request->u.servicereg.port); + if (count) + LogMsg("Client application[%d](%s) registered %d identical instances of service %##s port %u.", request->process_id, + request->pid_name, count+1, srv.c, mDNSVal16(request->u.servicereg.port)); + } -// 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; + LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START PID[%d](%s)", + request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, + mDNSVal16(request->u.servicereg.port), request->process_id, request->pid_name); -// for multi-domain default browsing -typedef struct browser_t - { - DNSQuestion q; - domainname domain; - struct browser_t *next; - } browser_t; + // We need to unconditionally set request->terminate, because even if we didn't successfully + // start any registrations right now, subsequent configuration changes may cause successful + // registrations to be added, and we'll need to cancel them before freeing this memory. + // We also need to set request->terminate first, before adding additional service instances, + // because the uds_validatelists uses the request->terminate function pointer to determine + // what kind of request this is, and therefore what kind of list validation is required. + request->terminate = regservice_termination_callback; -// 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; + err = register_service_instance(request, &d); -typedef struct +#if 0 + err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError; +#endif + if (!err) { - mStatus err; // Note: This field is in NETWORK byte order - int nwritten; - dnssd_sock_t sd; - } undelivered_error_t; + if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage); -typedef struct request_state - { - // connection structures - dnssd_sock_t sd; + if (!*domain) + { + DNameListElem *ptr; + // Note that we don't report errors for non-local, non-explicit domains + for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next) + if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid) + register_service_instance(request, &ptr->name); + } + } - // 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 + return(err); +} - // 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; +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNSServiceBrowse +#endif - //!!!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; +mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0; + request_state *req = question->QuestionContext; + reply_state *rep; + (void)m; // Unused -// 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; + if (answer->rrtype != kDNSType_PTR) + { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; } -typedef struct reply_state + if (mDNSOpaque16IsZero(question->TargetQID) && (question->BrowseThreshold > 0) && (question->CurrentAnswers >= question->BrowseThreshold)) { - // 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; + flags |= kDNSServiceFlagsThresholdReached; + } -typedef struct + if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError) { - domain_enum_t *all; - domain_enum_t *def; - request_state *rstate; - } enum_termination_t; + if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) + { + // Special support to enable the DNSServiceBrowse call made by Bonjour Browser + // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse + GenerateBonjourBrowserResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError); + goto bonjourbrowserhack; + } -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_ + 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; + } -// 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); +bonjourbrowserhack: -// initialization, setup/teardown functions + LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s", + req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", + mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer)); -// If a platform specifies its own PID file name, we use that -#ifndef PID_FILE -#define PID_FILE "/var/run/mDNSResponder.pid" -#endif + append_reply(req, rep); +} -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 mStatus add_domain_to_browser(request_state *info, const domainname *d) +{ + browser_t *b, *p; + mStatus err; -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 + for (p = info->u.browser.browsers; p; p = p->next) + { + if (SameDomainName(&p->domain, d)) + { debugf("add_domain_to_browser %##s already in list", d->c); return mStatus_AlreadyRegistered; } + } - // 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)) + b = mallocL("browser_t", sizeof(*b)); + if (!b) return mStatus_NoMemoryErr; + AssignDomainName(&b->domain, d); + err = mDNS_StartBrowse(&mDNSStorage, &b->q, &info->u.browser.regtype, d, info->u.browser.AnonData, info->u.browser.interface_id, info->flags, + info->u.browser.ForceMCast, (info->flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, info); + if (err) + { + LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c); + freeL("browser_t/add_domain_to_browser", b); + } + else + { + b->next = info->u.browser.browsers; + info->u.browser.browsers = b; + LogOperation("%3d: DNSServiceBrowse(%##s) START PID[%d](%s)", info->sd, b->q.qname.c, info->process_id, + info->pid_name); + LogMcastQ(&mDNSStorage, &b->q, info, q_start); + if (callExternalHelpers(info->u.browser.interface_id, &b->domain, info->flags)) { - my_perror("ERROR: could not add listen socket to event loop"); - goto error; + domainname tmp; + ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain); + LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()"); + external_start_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags); } - -#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; } + return err; +} -int udsserver_exit(void) +mDNSlocal void browse_termination_callback(request_state *info) +{ + if (info->u.browser.default_domain) { - 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 + // Stop the domain enumeration queries to discover the WAB legacy browse domains + LogInfo("%3d: DNSServiceBrowse Cancel WAB PID[%d](%s)", info->sd, info->process_id, info->pid_name); + uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY); + } + if (info->u.browser.AnonData) + freeL("Anonymous", (void *)info->u.browser.AnonData); + while (info->u.browser.browsers) + { + browser_t *ptr = info->u.browser.browsers; - if (PID_FILE[0]) unlink(PID_FILE); + if (callExternalHelpers(info->u.browser.interface_id, &ptr->domain, info->flags)) + { + domainname tmp; + ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain); + LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()"); + external_stop_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags); + } - return 0; + info->u.browser.browsers = ptr->next; + LogOperation("%3d: DNSServiceBrowse(%##s) STOP PID[%d](%s)", info->sd, ptr->q.qname.c, info->process_id, info->pid_name); + mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result + LogMcastQ(&mDNSStorage, &ptr->q, info, q_stop); + freeL("browser_t/browse_termination_callback", ptr); } +} -mDNSs32 udsserver_idle(mDNSs32 nextevent) - { - request_state *req = all_requests, *tmp, *prev = NULL; - reply_state *fptr; - transfer_state result; - mDNSs32 now = mDNS_TimeNow(&mDNSStorage); +mDNSlocal void udsserver_automatic_browse_domain_changed(const DNameListElem *const d, const mDNSBool add) +{ + request_state *request; + debugf("udsserver_automatic_browse_domain_changed: %s default browse domain %##s", add ? "Adding" : "Removing", d->name.c); + +#if APPLE_OSX_mDNSResponder + machserver_automatic_browse_domain_changed(&d->name, add); +#endif // APPLE_OSX_mDNSResponder - while(req) + for (request = all_requests; request; request = request->next) + { + if (request->terminate != browse_termination_callback) continue; // Not a browse operation + if (!request->u.browser.default_domain) continue; // Not an auto-browse operation + if (!d->uid || SystemUID(request->uid) || request->uid == d->uid) { - 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)) + browser_t **ptr = &request->u.browser.browsers; + while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next; + if (add) + { + // If we don't already have this domain in our list for this browse operation, add it now + if (!*ptr) add_domain_to_browser(request, &d->name); + else debugf("udsserver_automatic_browse_domain_changed %##s already in list, not re-adding", &d->name); + } + else { - while(req->replies) + if (!*ptr) LogMsg("udsserver_automatic_browse_domain_changed ERROR %##s not found", &d->name); + else { - if (req->replies->next) req->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing); - result = send_msg(req->replies); - if (result == t_complete) + DNameListElem *p; + for (p = AutoBrowseDomains; p; p=p->next) + if (!p->uid || SystemUID(request->uid) || request->uid == p->uid) + if (SameDomainName(&d->name, &p->name)) break; + if (p) debugf("udsserver_automatic_browse_domain_changed %##s still in list, not removing", &d->name); + else { - fptr = req->replies; - req->replies = req->replies->next; - freeL("udsserver_idle", fptr); - req->time_blocked = 0; // reset failure counter after successful send + browser_t *rem = *ptr; + *ptr = (*ptr)->next; + mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q); + freeL("browser_t/udsserver_automatic_browse_domain_changed", rem); } - 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) +mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + (void)m; // unused + if (result == mStatus_MemFree) { - 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); + // On shutdown, mDNS_Close automatically deregisters all records + // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record + // from the LocalDomainEnumRecords list, we do this here before we free the memory. + // (This should actually no longer be necessary, now that we do the proper cleanup in + // udsserver_exit. To confirm this, we'll log an error message if we do find a record that + // hasn't been cut from the list yet. If these messages don't appear, we can delete this code.) + ARListElem **ptr = &LocalDomainEnumRecords; + while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next; + if (*ptr) { *ptr = (*ptr)->next; LogMsg("FreeARElemCallback: Have to cut %s", ARDisplayString(m, rr)); } + mDNSPlatformMemFree(rr->RecordContext); + } +} - for (req = all_requests; req; req=req->next) - LogClientInfo(req); +// RegisterLocalOnlyDomainEnumPTR and DeregisterLocalOnlyDomainEnumPTR largely duplicate code in +// "FoundDomain" in uDNS.c for creating and destroying these special mDNSInterface_LocalOnly records. +// We may want to turn the common code into a subroutine. - now = mDNS_TimeNow(m); - LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now); +mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type) +{ + // allocate/register legacy and non-legacy _browse PTR record + mStatus err; + ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(*ptr)); + + debugf("Incrementing %s refcount for %##s", + (type == mDNS_DomainTypeBrowse ) ? "browse domain " : + (type == mDNS_DomainTypeRegistration ) ? "registration dom" : + (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c); + + mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr); + MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]); + AppendDNSNameString (&ptr->ar.namestorage, "local"); + AssignDomainName(&ptr->ar.resrec.rdata->u.name, d); + err = mDNS_Register(m, &ptr->ar); + if (err) + { + LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err); + mDNSPlatformMemFree(ptr); } + else + { + ptr->next = LocalDomainEnumRecords; + LocalDomainEnumRecords = ptr; + } +} -#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 DeregisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type) +{ + ARListElem **ptr = &LocalDomainEnumRecords; + domainname lhs; // left-hand side of PTR, for comparison -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); - } - } + debugf("Decrementing %s refcount for %##s", + (type == mDNS_DomainTypeBrowse ) ? "browse domain " : + (type == mDNS_DomainTypeRegistration ) ? "registration dom" : + (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c); -mDNSexport void udsserver_handle_configchange(void) - { - request_state *req; + MakeDomainNameFromDNSNameString(&lhs, mDNS_DomainTypeNames[type]); + AppendDNSNameString (&lhs, "local"); - for (req = all_requests; req; req = req->next) + while (*ptr) + { + if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, d) && SameDomainName((*ptr)->ar.resrec.name, &lhs)) { - if (req->service_registration) - { - service_instance *ptr; - for (ptr = req->service_registration->instances; ptr; ptr = ptr->next) - rename_service(ptr); - } - } + ARListElem *rem = *ptr; + *ptr = (*ptr)->next; + mDNS_Deregister(m, &rem->ar); + return; + } + else ptr = &(*ptr)->next; } - -mDNSlocal void connect_callback(void *info) +} + +mDNSlocal void AddAutoBrowseDomain(const mDNSu32 uid, const domainname *const name) +{ + DNameListElem *new = mDNSPlatformMemAllocate(sizeof(DNameListElem)); + if (!new) { LogMsg("ERROR: malloc"); return; } + AssignDomainName(&new->name, name); + new->uid = uid; + new->next = AutoBrowseDomains; + AutoBrowseDomains = new; + udsserver_automatic_browse_domain_changed(new, mDNStrue); +} + +mDNSlocal void RmvAutoBrowseDomain(const mDNSu32 uid, const domainname *const name) +{ + DNameListElem **p = &AutoBrowseDomains; + while (*p && (!SameDomainName(&(*p)->name, name) || (*p)->uid != uid)) p = &(*p)->next; + if (!*p) LogMsg("RmvAutoBrowseDomain: Got remove event for domain %##s not in list", name->c); + else { - 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); + DNameListElem *ptr = *p; + *p = ptr->next; + udsserver_automatic_browse_domain_changed(ptr, mDNSfalse); + mDNSPlatformMemFree(ptr); + } +} - if (sd == dnssd_InvalidSocket) +mDNSlocal void SetPrefsBrowseDomains(mDNS *m, DNameListElem *browseDomains, mDNSBool add) +{ + DNameListElem *d; + for (d = browseDomains; d; d = d->next) + { + if (add) { - 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 + RegisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse); + AddAutoBrowseDomain(d->uid, &d->name); + } + else { - 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; + DeregisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse); + RmvAutoBrowseDomain(d->uid, &d->name); + } } +} -// 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. +#if APPLE_OSX_mDNSResponder -// 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 UpdateDeviceInfoRecord(mDNS *const m) +{ + int num_autoname = 0; + request_state *req; + for (req = all_requests; req; req = req->next) + if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname) + num_autoname++; -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) + // If DeviceInfo record is currently registered, see if we need to deregister it + if (m->DeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered) + if (num_autoname == 0 || !SameDomainLabelCS(m->DeviceInfo.resrec.name->c, m->nicelabel.c)) { - LogMsg("ERROR: handle_query_request - transfer state != t_complete"); - goto error; + LogOperation("UpdateDeviceInfoRecord Deregister %##s", m->DeviceInfo.resrec.name); + mDNS_Deregister(m, &m->DeviceInfo); } - ptr = rstate->msgdata; - if (!ptr) + + // If DeviceInfo record is not currently registered, see if we need to register it + if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) + if (num_autoname > 0) { - LogMsg("ERROR: handle_query_request - NULL msgdata"); - goto error; + mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL); + ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain); + m->DeviceInfo.resrec.rdlength = initializeDeviceInfoTXT(m, m->DeviceInfo.resrec.rdata->u.data); + LogOperation("UpdateDeviceInfoRecord Register %##s", m->DeviceInfo.resrec.name); + mDNS_Register(m, &m->DeviceInfo); } - - 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; +} +#else // APPLE_OSX_mDNSResponder +mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m) +{ + (void)m; // unused +} +#endif // APPLE_OSX_mDNSResponder + +mDNSexport void udsserver_handle_configchange(mDNS *const m) +{ + request_state *req; + service_instance *ptr; + DNameListElem *RegDomains = NULL; + DNameListElem *BrowseDomains = NULL; + DNameListElem *p; - q = mallocL("DNSQuestion", sizeof(DNSQuestion)); - if (!q) FatalError("ERROR: handle_query - malloc"); - bzero(q, sizeof(DNSQuestion)); + UpdateDeviceInfoRecord(m); - 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; - } + // For autoname services, see if the default service name has changed, necessitating an automatic update + for (req = all_requests; req; req = req->next) + if (req->terminate == regservice_termination_callback) + if (req->u.servicereg.autoname && !SameDomainLabelCS(req->u.servicereg.name.c, m->nicelabel.c)) + { + req->u.servicereg.name = m->nicelabel; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + { + ptr->renameonmemfree = 1; + if (ptr->clientnotified) SendServiceRemovalNotification(&ptr->srs); + LogInfo("udsserver_handle_configchange: Calling deregister for Service %##s", ptr->srs.RR_PTR.resrec.name->c); + if (mDNS_DeregisterService_drt(m, &ptr->srs, mDNS_Dereg_rapid)) + regservice_callback(m, &ptr->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately + } + } + + // Let the platform layer get the current DNS information + mDNS_Lock(m); + mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains, mDNSfalse); + mDNS_Unlock(m); -mDNSlocal void handle_resolve_request(request_state *rstate) + // Any automatic registration domains are also implicitly automatic browsing domains + if (RegDomains) SetPrefsBrowseDomains(m, RegDomains, mDNStrue); // Add the new list first + if (AutoRegistrationDomains) SetPrefsBrowseDomains(m, AutoRegistrationDomains, mDNSfalse); // Then clear the old list + + // Add any new domains not already in our AutoRegistrationDomains list + for (p=RegDomains; p; p=p->next) { - 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) + DNameListElem **pp = &AutoRegistrationDomains; + while (*pp && ((*pp)->uid != p->uid || !SameDomainName(&(*pp)->name, &p->name))) pp = &(*pp)->next; + if (!*pp) // If not found in our existing list, this is a new default registration domain { - LogMsg("ERROR: handle_resolve_request - transfer state != t_complete"); - abort_request(rstate); - unlink_request(rstate); - return; + RegisterLocalOnlyDomainEnumPTR(m, &p->name, mDNS_DomainTypeRegistration); + udsserver_default_reg_domain_changed(p, mDNStrue); } - - // extract the data from the message - ptr = rstate->msgdata; - if (!ptr) + else // else found same domainname in both old and new lists, so no change, just delete old copy { - LogMsg("ERROR: handle_resolve_request - NULL msgdata"); - abort_request(rstate); - unlink_request(rstate); - return; + DNameListElem *del = *pp; + *pp = (*pp)->next; + mDNSPlatformMemFree(del); } - 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; + // Delete any domains in our old AutoRegistrationDomains list that are now gone + while (AutoRegistrationDomains) + { + DNameListElem *del = AutoRegistrationDomains; + AutoRegistrationDomains = AutoRegistrationDomains->next; // Cut record from list FIRST, + DeregisterLocalOnlyDomainEnumPTR(m, &del->name, mDNS_DomainTypeRegistration); + udsserver_default_reg_domain_changed(del, mDNSfalse); // before calling udsserver_default_reg_domain_changed() + mDNSPlatformMemFree(del); + } - 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; } + // Now we have our new updated automatic registration domain list + AutoRegistrationDomains = RegDomains; - // set up termination info - term = mallocL("handle_resolve_request", sizeof(resolve_termination_t)); - bzero(term, sizeof(*term)); - if (!term) FatalError("ERROR: malloc"); + // Add new browse domains to internal list + if (BrowseDomains) SetPrefsBrowseDomains(m, BrowseDomains, mDNStrue); - // 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) + // Remove old browse domains from internal list + if (SCPrefBrowseDomains) + { + SetPrefsBrowseDomains(m, SCPrefBrowseDomains, mDNSfalse); + while (SCPrefBrowseDomains) { - freeL("handle_resolve_request", term); - rstate->terminate = NULL; // prevent abort_request() from invoking termination callback + DNameListElem *fptr = SCPrefBrowseDomains; + SCPrefBrowseDomains = SCPrefBrowseDomains->next; + mDNSPlatformMemFree(fptr); } - if (deliver_error(rstate, err) < 0 || err) + } + + // Replace the old browse domains array with the new array + SCPrefBrowseDomains = BrowseDomains; +} + +mDNSlocal void AutomaticBrowseDomainChange(mDNS *const m, DNSQuestion *q, const ResourceRecord *const answer, QC_result AddRecord) +{ + (void)m; // unused; + (void)q; // unused + + LogOperation("AutomaticBrowseDomainChange: %s automatic browse domain %##s", + AddRecord ? "Adding" : "Removing", answer->rdata->u.name.c); + + if (AddRecord) AddAutoBrowseDomain(0, &answer->rdata->u.name); + else RmvAutoBrowseDomain(0, &answer->rdata->u.name); +} + +mDNSlocal mStatus handle_browse_request(request_state *request) +{ + char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; + domainname typedn, d, temp; + mDNSs32 NumSubTypes; + char *AnonData = mDNSNULL; + mStatus err = mStatus_NoError; + int AnonDataLen; + + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + + // The browse is scoped to a specific interface index, but the + // interface is not currently in our list. + if (interfaceIndex && !InterfaceID) + { + // If it's one of the specially defined inteface index values, just return an error. + if (PreDefinedInterfaceIndex(interfaceIndex)) { - abort_request(rstate); - unlink_request(rstate); + LogMsg("ERROR: handle_browse_request: bad interfaceIndex %d", interfaceIndex); + return(mStatus_BadParamErr); } - return; -bad_param: - deliver_error(rstate, mStatus_BadParamErr); - abort_request(rstate); - unlink_request(rstate); + // Otherwise, use the specified interface index value and the browse will + // be applied to that interface when it comes up. + InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; + LogInfo("handle_browse_request: browse pending for interface index %d", interfaceIndex); } - -mDNSlocal void resolve_termination_callback(void *context) + + if (get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) return(mStatus_BadParamErr); + + if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + + request->flags = flags; + typedn.c[0] = 0; + NumSubTypes = ChopSubTypes(regtype, &AnonData); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0 || NumSubTypes > 1) + return(mStatus_BadParamErr); + AnonDataLen = 0; + if (AnonData) { - resolve_termination_t *term = context; - request_state *rs; - - if (!term) + AnonDataLen = strlen(AnonData); + if (AnonDataLen > MAX_ANONYMOUS_DATA) { - LogMsg("ERROR: resolve_termination_callback: double termination"); - return; + LogMsg("handle_browse_request: AnonDataLen %d", AnonDataLen); + return(mStatus_BadParamErr); } - 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; + // Account for the null byte + AnonDataLen += 1; + } + if (NumSubTypes == 1) + { + if (!AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1 + AnonDataLen)) + return(mStatus_BadParamErr); } -mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) - { + if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr); + + if (!MakeDomainNameFromDNSNameString(&temp, regtype)) return(mStatus_BadParamErr); + // For over-long service types, we only allow domain "local" + if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrCopy(domain, "local."); + + // Set up browser info + request->u.browser.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; + request->u.browser.interface_id = InterfaceID; + AssignDomainName(&request->u.browser.regtype, &typedn); + request->u.browser.default_domain = !domain[0]; + request->u.browser.browsers = NULL; + + LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START PID[%d](%s)", + request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain, request->process_id, request->pid_name); + + if (request->u.browser.default_domain) + { + // Start the domain enumeration queries to discover the WAB browse domains + LogInfo("%3d: DNSServiceBrowse Start WAB PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY); + } + request->u.browser.AnonData = mDNSNULL; + if (AnonData) + { + int len = strlen(AnonData) + 1; + request->u.browser.AnonData = mallocL("Anonymous", len); + if (!request->u.browser.AnonData) + return mStatus_NoMemoryErr; + else + mDNSPlatformMemCopy((void *)request->u.browser.AnonData, AnonData, len); + } + // We need to unconditionally set request->terminate, because even if we didn't successfully + // start any browses right now, subsequent configuration changes may cause successful + // browses to be added, and we'll need to cancel them before freeing this memory. + request->terminate = browse_termination_callback; + + if (domain[0]) + { + if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr); + err = add_domain_to_browser(request, &d); + } + else + { + DNameListElem *sdom; + for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next) + if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid) + { + err = add_domain_to_browser(request, &sdom->name); + if (err) + { + if (SameDomainName(&sdom->name, &localdomain)) break; + else err = mStatus_NoError; // suppress errors for non-local "default" domains + } + } + } + + return(err); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNSServiceResolve +#endif + +mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ size_t len = 0; char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME]; char *data; - transfer_state result; reply_state *rep; - request_state *rs = question->QuestionContext; - resolve_termination_t *res = rs->termination_context; + request_state *req = question->QuestionContext; (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 - + LogOperation("%3d: DNSServiceResolve(%##s) %s %s", req->sd, question->qname.c, AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); + + if (!AddRecord) + { + if (req->u.resolve.srv == answer) req->u.resolve.srv = mDNSNULL; + if (req->u.resolve.txt == answer) req->u.resolve.txt = mDNSNULL; + return; + } + + if (answer->rrtype == kDNSType_SRV) req->u.resolve.srv = answer; + if (answer->rrtype == kDNSType_TXT) req->u.resolve.txt = answer; + + if (!req->u.resolve.txt || !req->u.resolve.srv) return; // only deliver result to client if we have both answers + ConvertDomainNameToCString(answer->name, fullname); - ConvertDomainNameToCString(&res->srvdata.target, target); + ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target); // calculate reply length len += sizeof(DNSServiceFlags); - len += sizeof(uint32_t); // interface index + len += sizeof(mDNSu32); // interface index len += sizeof(DNSServiceErrorType); len += strlen(fullname) + 1; len += strlen(target) + 1; - len += 2 * sizeof(uint16_t); // port, txtLen - len += res->txtlen; - + len += 2 * sizeof(mDNSu16); // port, txtLen + len += req->u.resolve.txt->rdlength; + // allocate/init reply header - rep = create_reply(resolve_reply_op, len, rs); + rep = create_reply(resolve_reply_op, len, req); rep->rhdr->flags = dnssd_htonl(0); - rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID)); + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse)); rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); - data = rep->sdata; - + data = (char *)&rep->rhdr[1]; + // 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) + *data++ = req->u.resolve.srv->rdata->u.srv.port.b[0]; + *data++ = req->u.resolve.srv->rdata->u.srv.port.b[1]; + put_uint16(req->u.resolve.txt->rdlength, &data); + put_rdata (req->u.resolve.txt->rdlength, req->u.resolve.txt->rdata->u.data, &data); + + LogOperation("%3d: DNSServiceResolve(%s) RESULT %s:%d", req->sd, fullname, target, mDNSVal16(req->u.resolve.srv->rdata->u.srv.port)); + append_reply(req, rep); +} + +mDNSlocal void resolve_termination_callback(request_state *request) +{ + LogOperation("%3d: DNSServiceResolve(%##s) STOP PID[%d](%s)", request->sd, request->u.resolve.qtxt.qname.c, request->process_id, request->pid_name); + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt); + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_stop); + if (request->u.resolve.external_advertise) + external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags); +} + +mDNSlocal mStatus handle_resolve_request(request_state *request) +{ + char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; + domainname fqdn; + mStatus err; + + // extract the data from the message + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID; + + // Map kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P + // flag set so that the resolve will run over P2P interfaces that are not yet created. + if (interfaceIndex == kDNSServiceInterfaceIndexP2P) + { + LogOperation("handle_resolve_request: mapping kDNSServiceInterfaceIndexP2P to kDNSServiceInterfaceIndexAny + kDNSServiceFlagsIncludeP2P"); + flags |= kDNSServiceFlagsIncludeP2P; + interfaceIndex = kDNSServiceInterfaceIndexAny; + } + + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + + // The operation is scoped to a specific interface index, but the + // interface is not currently in our list. + if (interfaceIndex && !InterfaceID) + { + // If it's one of the specially defined inteface index values, just return an error. + if (PreDefinedInterfaceIndex(interfaceIndex)) { - abort_request(rs); - unlink_request(rs); - freeL("resolve_result_callback", rep); + LogMsg("ERROR: handle_resolve_request: bad interfaceIndex %d", interfaceIndex); + return(mStatus_BadParamErr); } - else if (result == t_complete) freeL("resolve_result_callback", rep); - else append_reply(rs, rep); + + // Otherwise, use the specified interface index value and the operation will + // be applied to that interface when it comes up. + InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; + LogInfo("handle_resolve_request: resolve pending for interface index %d", interfaceIndex); } -// 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 + if (get_string(&request->msgptr, request->msgend, name, 256) < 0 || + get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) + { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } - 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); + if (!request->msgptr) { LogMsg("%3d: DNSServiceResolve(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0); - rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID)); - rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); + if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0) + { LogMsg("ERROR: handle_resolve_request bad “%s” “%s” “%s”", name, regtype, domain); return(mStatus_BadParamErr); } - 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); + mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve)); - append_reply(req, rep); - return; + request->flags = flags; + + // format questions + request->u.resolve.qsrv.InterfaceID = InterfaceID; + request->u.resolve.qsrv.flags = flags; + request->u.resolve.qsrv.Target = zeroAddr; + AssignDomainName(&request->u.resolve.qsrv.qname, &fqdn); + request->u.resolve.qsrv.qtype = kDNSType_SRV; + request->u.resolve.qsrv.qclass = kDNSClass_IN; + request->u.resolve.qsrv.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + request->u.resolve.qsrv.ExpectUnique = mDNStrue; + request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.resolve.qsrv.SuppressUnusable = mDNSfalse; + request->u.resolve.qsrv.DenyOnCellInterface = mDNSfalse; + request->u.resolve.qsrv.DenyOnExpInterface = mDNSfalse; + request->u.resolve.qsrv.SearchListIndex = 0; + request->u.resolve.qsrv.AppendSearchDomains = 0; + request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse; + request->u.resolve.qsrv.TimeoutQuestion = 0; + request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0; + request->u.resolve.qsrv.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + request->u.resolve.qsrv.ValidationRequired = 0; + request->u.resolve.qsrv.ValidatingResponse = 0; + request->u.resolve.qsrv.ProxyQuestion = 0; + request->u.resolve.qsrv.qnameOrig = mDNSNULL; + request->u.resolve.qsrv.AnonInfo = mDNSNULL; + request->u.resolve.qsrv.pid = request->process_id; + request->u.resolve.qsrv.QuestionCallback = resolve_result_callback; + request->u.resolve.qsrv.QuestionContext = request; + + request->u.resolve.qtxt.InterfaceID = InterfaceID; + request->u.resolve.qtxt.flags = flags; + request->u.resolve.qtxt.Target = zeroAddr; + AssignDomainName(&request->u.resolve.qtxt.qname, &fqdn); + request->u.resolve.qtxt.qtype = kDNSType_TXT; + request->u.resolve.qtxt.qclass = kDNSClass_IN; + request->u.resolve.qtxt.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + request->u.resolve.qtxt.ExpectUnique = mDNStrue; + request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.resolve.qtxt.SuppressUnusable = mDNSfalse; + request->u.resolve.qtxt.DenyOnCellInterface = mDNSfalse; + request->u.resolve.qtxt.DenyOnExpInterface = mDNSfalse; + request->u.resolve.qtxt.SearchListIndex = 0; + request->u.resolve.qtxt.AppendSearchDomains = 0; + request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse; + request->u.resolve.qtxt.TimeoutQuestion = 0; + request->u.resolve.qtxt.WakeOnResolve = 0; + request->u.resolve.qtxt.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + request->u.resolve.qtxt.ValidationRequired = 0; + request->u.resolve.qtxt.ValidatingResponse = 0; + request->u.resolve.qtxt.ProxyQuestion = 0; + request->u.resolve.qtxt.qnameOrig = mDNSNULL; + request->u.resolve.qtxt.AnonInfo = mDNSNULL; + request->u.resolve.qtxt.pid = request->process_id; + request->u.resolve.qtxt.QuestionCallback = resolve_result_callback; + request->u.resolve.qtxt.QuestionContext = request; + + request->u.resolve.ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); + + request->u.resolve.external_advertise = mDNSfalse; + +#if 0 + if (!AuthorizedDomain(request, &fqdn, AutoBrowseDomains)) return(mStatus_NoError); +#endif + + // ask the questions + LogOperation("%3d: DNSServiceResolve(%X %d %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, + request->u.resolve.qsrv.qname.c, request->process_id, request->pid_name); + err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv); + + if (!err) + { + err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt); + if (err) + { + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + } + else + { + request->terminate = resolve_termination_callback; + LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_start); + if (callExternalHelpers(InterfaceID, &fqdn, flags)) + { + request->u.resolve.external_advertise = mDNStrue; + LogInfo("handle_resolve_request: calling external_start_resolving_service()"); + external_start_resolving_service(InterfaceID, &fqdn, flags); + } + } } -mDNSlocal void question_termination_callback(void *context) + return(err); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNSServiceQueryRecord +#endif + +// mDNS operation functions. Each operation has 3 associated functions - a request handler that parses +// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback +// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts +// the mDNSCore operation if the client dies or closes its socket. + +// Returns -1 to tell the caller that it should not try to reissue the query anymore +// Returns 1 on successfully appending a search domain and the caller should reissue the new query +// Returns 0 when there are no more search domains and the caller should reissue the query +mDNSlocal int AppendNewSearchDomain(mDNS *const m, DNSQuestion *question) +{ + domainname *sd; + mStatus err; + + // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all + // the domains and should try the single label query directly on the wire. + if (question->SearchListIndex == -1) { - 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); + LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype)); + return -1; } -// 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 (!question->AppendSearchDomains) + { + LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype)); + return -1; + } -// 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); - } + // Save the original name, before we modify them below. + if (!question->qnameOrig) + { + question->qnameOrig = mallocL("AppendNewSearchDomain", sizeof(domainname)); + if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!! malloc failure"); return -1; } + question->qnameOrig->c[0] = 0; + AssignDomainName(question->qnameOrig, &question->qname); + LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c); + } -// 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 + sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains); + // We use -1 to indicate that we have searched all the domains and should try the single label + // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value + if (question->SearchListIndex == -1) + { + LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1"); + return -1; + } -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; - } + // Not a common case. Perhaps, we should try the next search domain if it exceeds ? + if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME) + { + LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd)); + return -1; + } - // 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); + // if there are no more search domains and we have already tried this question + // without appending search domains, then we are done. + if (!sd && !ApplySearchDomainsFirst(question)) + { + LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype)); + return -1; } -// 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) + // Stop the question before changing the name as negative cache entries could be pointing at this question. + // Even if we don't change the question in the case of returning 0, the caller is going to restart the + // question. + err = mDNS_StopQuery(&mDNSStorage, question); + if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); } + + AssignDomainName(&question->qname, question->qnameOrig); + if (sd) { - 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; - } + AppendDomainName(&question->qname, sd); + LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex); + return 1; + } - // 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); - } - } - } + // Try the question as single label + LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype)); + return 0; +} -// 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); - } +#if APPLE_OSX_mDNSResponder -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 mDNSBool DomainInSearchList(const domainname *domain, mDNSBool excludeLocal) +{ + const SearchListElem *s; + int qcount, scount; -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) + qcount = CountLabels(domain); + for (s=SearchList; s; s=s->next) { - 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) + if (excludeLocal && SameDomainName(&s->domain, &localdomain)) + continue; + scount = CountLabels(&s->domain); + if (qcount >= scount) { - LogMsg("ERROR: handle_regservice_request - transfer state != t_complete"); - abort_request(request); - unlink_request(request); - return; + // Note: When qcount == scount, we do a complete match of the domain + // which is expected by the callers. + const domainname *d = SkipLeadingLabels(domain, (qcount - scount)); + if (SameDomainName(&s->domain, d)) + { + return mDNStrue; + } } + } + return mDNSfalse; +} + +// The caller already checks that this is a dotlocal question. +mDNSlocal mDNSBool ShouldDeliverNegativeResponse(mDNS *const m, DNSQuestion *question) +{ + mDNSu16 qtype; + + // If the question matches the search domain exactly or the search domain is a + // subdomain of the question, it is most likely a valid unicast domain and hence + // don't suppress negative responses. + // + // If the user has configured ".local" as a search domain, we don't want + // to deliver a negative response for names ending in ".local" as that would + // prevent bonjour discovery. Passing mDNStrue for the last argument excludes + // ".local" search domains. + if (DomainInSearchList(&question->qname, mDNStrue)) + { + LogOperation("ShouldDeliverNegativeResponse: Question %##s (%s) in SearchList", question->qname.c, DNSTypeName(question->qtype)); + return mDNStrue; + } - service = mallocL("service_info", sizeof(*service)); - if (!service) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; } + // Deliver negative response for A/AAAA if there was a positive response for AAAA/A respectively. + if (question->qtype != kDNSType_A && question->qtype != kDNSType_AAAA) + { + LogOperation("ShouldDeliverNegativeResponse: Question %##s (%s) not answering local question with negative unicast response", + question->qname.c, DNSTypeName(question->qtype)); + return mDNSfalse; + } + qtype = (question->qtype == kDNSType_A ? kDNSType_AAAA : kDNSType_A); + if (!mDNS_CheckForCacheRecord(m, question, qtype)) + { + LogOperation("ShouldDeliverNegativeResponse:Question %##s (%s) not answering local question with negative unicast response" + " (can't find positive record)", question->qname.c, DNSTypeName(question->qtype)); + return mDNSfalse; + } + LogOperation("ShouldDeliverNegativeResponse:Question %##s (%s) answering local with negative unicast response (found positive record)", + question->qname.c, DNSTypeName(question->qtype)); + return mDNStrue; +} + +// Workaround for networks using Microsoft Active Directory using "local" as a private internal +// top-level domain +mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err) +{ +#ifndef UNICAST_DISABLED + extern domainname ActiveDirectoryPrimaryDomain; + DNSQuestion **question2; + #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp")) + #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname))) + + question2 = mDNSNULL; + if (request->hdr.op == query_request) + question2 = &request->u.queryrecord.q2; + else if (request->hdr.op == addrinfo_request) + { + if (q->qtype == kDNSType_A) + question2 = &request->u.addrinfo.q42; + else if (q->qtype == kDNSType_AAAA) + question2 = &request->u.addrinfo.q62; + } + if (!question2) + { + LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mStatus_BadParamErr; + } - 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; } + // Sanity check: If we already sent an additonal query, we don't need to send one more. + // + // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function + // is called to see whether a unicast query should be sent or not. + // + // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it + // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to + // send the additional query. + // + // Thus, it should not be called more than once. + if (*question2) + { + LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype)); + return err; + } - if (!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); + if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain)) + if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q)) + { + DNSQuestion *q2; + int labels = CountLabels(&q->qname); + q2 = mallocL("DNSQuestion", sizeof(DNSQuestion)); + if (!q2) FatalError("ERROR: SendAdditionalQuery malloc"); + *question2 = q2; + *q2 = *q; + q2->InterfaceID = mDNSInterface_Unicast; + q2->ExpectUnique = mDNStrue; + // Always set the QuestionContext to indicate that this question should be stopped + // before freeing. Don't rely on "q". + q2->QuestionContext = request; + // If the query starts as a single label e.g., somehost, and we have search domains with .local, + // queryrecord_result_callback calls this function when .local is appended to "somehost". + // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at + // "somehost". We need to copy that information so that when we retry with a different search + // domain e.g., mycompany.local, we get "somehost.mycompany.local". + if (q->qnameOrig) + { + (*question2)->qnameOrig = mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig)); + if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!! malloc failure"); return mStatus_NoMemoryErr; } + (*question2)->qnameOrig->c[0] = 0; + AssignDomainName((*question2)->qnameOrig, q->qnameOrig); + LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c); + } + // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel. + // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query. + // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP + // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser) + // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the + // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries + // for names in the "local" domain will be safely answered privately before they hit the root name servers. + // Note that in the "my-small-company.local" example above there will typically be an SOA record for + // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case. + // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either + // of those, we don't want do the SOA check for the local + if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname, mDNSfalse)) + { + AssignDomainName(&q2->qname, &localdomain); + q2->qtype = kDNSType_SOA; + q2->LongLived = mDNSfalse; + q2->ForceMCast = mDNSfalse; + q2->ReturnIntermed = mDNStrue; + // Don't append search domains for the .local SOA query + q2->AppendSearchDomains = 0; + q2->AppendLocalSearchDomains = 0; + q2->RetryWithSearchDomains = mDNSfalse; + q2->SearchListIndex = 0; + q2->TimeoutQuestion = 0; + q2->AnonInfo = mDNSNULL; + q2->pid = request->process_id; + } + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype)); + err = mDNS_StartQuery(&mDNSStorage, q2); + if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err); + } + return(err); +#else // !UNICAST_DISABLED + (void) q; + (void) request; + (void) err; + + return mStatus_NoError; +#endif // !UNICAST_DISABLED +} +#endif // APPLE_OSX_mDNSResponder + +// This function tries to append a search domain if valid and possible. If so, returns true. +mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req, QC_result AddRecord) +{ + int result; + // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no + // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so + // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch + // RetryWithSearchDomains which may or may not be set. + // + // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and + // is a valid question for appending search domains, retry by appending domains + + if ((AddRecord != QC_suppressed) && question->SearchListIndex != -1 && question->AppendSearchDomains) + { + question->RetryWithSearchDomains = 0; + result = AppendNewSearchDomain(m, question); + // As long as the result is either zero or 1, we retry the question. If we exahaust the search + // domains (result is zero) we try the original query (as it was before appending the search + // domains) as such on the wire as a last resort if we have not tried them before. For queries + // with more than one label, we have already tried them before appending search domains and + // hence don't retry again + if (result != -1) + { + mStatus err; + err = mDNS_StartQuery(m, question); + if (!err) + { + LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype)); + // If the result was zero, it meant that there are no search domains and we just retried the question + // as a single label and we should not retry with search domains anymore. + if (!result) question->SearchListIndex = -1; + return mDNStrue; + } + else + { + LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); + // We have already stopped the query and could not restart. Reset the appropriate pointers + // so that we don't call stop again when the question terminates + question->QuestionContext = mDNSNULL; + } } + } else - reset_connected_rstate(request); // prepare to receive add/remove messages + { + LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, AddRecord, question->SearchListIndex, question->AppendSearchDomains); + } + return mDNSfalse; +} - return; +mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord, + DNSServiceErrorType error) +{ + char name[MAX_ESCAPED_DOMAIN_NAME]; + size_t len; + DNSServiceFlags flags = 0; + reply_state *rep; + char *data; + + ConvertDomainNameToCString(answer->name, name); + + LogOperation("%3d: %s(%##s, %s) %s %s", req->sd, + req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo", + question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); + + len = sizeof(DNSServiceFlags); // calculate reply data length + len += sizeof(mDNSu32); // interface index + len += sizeof(DNSServiceErrorType); + len += strlen(name) + 1; + len += 3 * sizeof(mDNSu16); // type, class, rdlen + len += answer->rdlength; + len += sizeof(mDNSu32); // TTL -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); + rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req); + + if (AddRecord) + flags |= kDNSServiceFlagsAdd; + if (question->ValidationStatus != 0) + { + error = kDNSServiceErr_NoError; + if (question->ValidationRequired && question->ValidationState == DNSSECValDone) + { + switch (question->ValidationStatus) //Set the dnssec flags to be passed on to the Apps here + { + case DNSSEC_Secure: + flags |= kDNSServiceFlagsSecure; + break; + case DNSSEC_Insecure: + flags |= kDNSServiceFlagsInsecure; + break; + case DNSSEC_Indeterminate: + flags |= kDNSServiceFlagsIndeterminate; + break; + case DNSSEC_Bogus: + flags |= kDNSServiceFlagsBogus; + break; + default: + LogMsg("queryrecord_result_reply unknown status %d for %##s", question->ValidationStatus, question->qname.c); + } + } } + + rep->rhdr->flags = dnssd_htonl(flags); + // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the + // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions + // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we + // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the + // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in + // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords + // should not have existed to answer this question if the corresponding interface is not valid. + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue)); + rep->rhdr->error = dnssd_htonl(error); + + data = (char *)&rep->rhdr[1]; + + put_string(name, &data); + put_uint16(answer->rrtype, &data); + put_uint16(answer->rrclass, &data); + put_uint16(answer->rdlength, &data); + // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata + // function just does a blind memory copy without regard to structures that may have holes in them. + if (answer->rdlength) + if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer)) + LogMsg("queryrecord_result_reply putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data); + data += answer->rdlength; + put_uint32(AddRecord ? answer->rroriginalttl : 0, &data); -// 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()) + append_reply(req, rep); + // Stop the question, if we just timed out + if (error == kDNSServiceErr_Timeout) + { + mDNS_StopQuery(m, question); + // Reset the pointers so that we don't call stop on termination + question->QuestionContext = mDNSNULL; + } + else if ((AddRecord == QC_add) && req->hdr.op == addrinfo_request) + { + // Note: We count all answers including LocalOnly e.g., /etc/hosts. If we + // exclude that, v4ans/v6ans will be zero and we would wrongly think that + // we did not answer questions and setup the status to deliver triggers. + if (question->qtype == kDNSType_A) + req->u.addrinfo.v4ans = 1; + if (question->qtype == kDNSType_AAAA) + req->u.addrinfo.v6ans = 1; + } + else if ((AddRecord == QC_add) && req->hdr.op == query_request) + { + if (question->qtype == kDNSType_A || question->qtype == kDNSType_AAAA) + req->u.queryrecord.ans = 1; + } -mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) +#if APPLE_OSX_mDNSResponder +#if !NO_WCF + CHECK_WCF_FUNCTION(WCFIsServerRunning) { - 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; } + struct xucred x; + socklen_t xucredlen = sizeof(x); - 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 (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0) + { + if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && + (x.cr_version == XUCRED_VERSION)) + { + struct sockaddr_storage addr; + const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data; + addr.ss_len = 0; + if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA) + { + if (answer->rrtype == kDNSType_A) + { + struct sockaddr_in *sin = (struct sockaddr_in *)&addr; + sin->sin_port = 0; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer)) + LogMsg("queryrecord_result_reply: WCF AF_INET putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in); + addr.ss_family = AF_INET; + } + } + else if (answer->rrtype == kDNSType_AAAA) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; + sin6->sin6_port = 0; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer)) + LogMsg("queryrecord_result_reply: WCF AF_INET6 putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in6); + addr.ss_family = AF_INET6; + } + } + if (addr.ss_len) + { + debugf("queryrecord_result_reply: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len); + CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) + { + WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid); + } + } + } + else if (answer->rrtype == kDNSType_CNAME) + { + domainname cname; + char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; + if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer)) + LogMsg("queryrecord_result_reply: WCF CNAME putRData failed"); + else + { + ConvertDomainNameToCString(&cname, cname_cstr); + CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) + { + WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid); + } + } + } + } + else my_perror("queryrecord_result_reply: ERROR: getsockopt LOCAL_PEERCRED"); + } + } +#endif +#endif +} - 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) +mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + request_state *req = question->QuestionContext; + DNSServiceErrorType error = kDNSServiceErr_NoError; + DNSQuestion *q = mDNSNULL; + +#if APPLE_OSX_mDNSResponder + { + // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not + // get any callbacks from the core after this. + if (!req) { - if (instance->rename_on_memfree) + LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return; + } + if (req->hdr.op == query_request && question == req->u.queryrecord.q2) + q = &req->u.queryrecord.q; + else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42) + q = &req->u.addrinfo.q4; + else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62) + q = &req->u.addrinfo.q6; + + if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname)) + { + mStatus err; + domainname *orig = question->qnameOrig; + + LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c); + mDNS_StopQuery(m, question); + question->QuestionContext = mDNSNULL; + + // We got a negative response for the SOA record indicating that .local does not exist. + // But we might have other search domains (that does not end in .local) that can be + // appended to this question. In that case, we want to retry the question. Otherwise, + // we don't want to try this question as unicast. + if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains) { - 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 + LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c); + return; } - else + + // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query + // + // Note: When we copy the original question, we copy everything including the AppendSearchDomains, + // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is + // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in + // SendAdditionalQuery as to how qnameOrig gets initialized. + *question = *q; + question->InterfaceID = mDNSInterface_Unicast; + question->ExpectUnique = mDNStrue; + question->qnameOrig = orig; + + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext); + + // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above. + // Hence, we need to set it explicitly here. + question->QuestionContext = req; + err = mDNS_StartQuery(m, question); + if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); + + // If we got a positive response to local SOA, then try the .local question as unicast + if (answer->RecordType != kDNSRecordTypePacketNegative) return; + + // Fall through and get the next search domain. The question is pointing at .local + // and we don't want to try that. Try the next search domain. Don't try with local + // search domains for the unicast question anymore. + // + // Note: we started the question above which will be stopped immediately (never sent on the wire) + // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the + // question has already started. + question->AppendLocalSearchDomains = 0; + } + + if (q && AddRecord && AddRecord != QC_dnssec && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength) + { + // If we get a negative response to the unicast query that we sent above, retry after appending search domains + // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here. + // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended. + // To keep things simple, we handle unicast ".local" separately here. + LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype)); + if (RetryQuestionWithSearchDomains(m, question, req, AddRecord)) + return; + if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname)) { - free_service_instance(instance); - return; + // If "local" is the last search domain, we need to stop the question so that we don't send the "local" + // question on the wire as we got a negative response for the local SOA. But, we can't stop the question + // yet as we may have to timeout the question (done by the "core") for which we need to leave the question + // in the list. We leave it disabled so that it does not hit the wire. + LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + question->ThisQInterval = 0; } } - 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) + // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search + // domains to append for "q2". In all cases, fall through and deliver the response + } +#endif // APPLE_OSX_mDNSResponder + + // If a query is being suppressed for some reason, we don't have to do any other + // processing. + // + // Note: We don't check for "SuppressQuery" and instead use QC_suppressed because + // the "core" needs to temporarily turn off SuppressQuery to answer this query. + if (AddRecord == QC_suppressed) + { + LogInfo("queryrecord_result_callback: Suppressed question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + queryrecord_result_reply(m, req, question, answer, AddRecord, kDNSServiceErr_NoSuchRecord); + return; + } + + if (answer->RecordType == kDNSRecordTypePacketNegative) + { + // If this question needs to be timed out and we have reached the stop time, mark + // the error as timeout. It is possible that we might get a negative response from an + // external DNS server at the same time when this question reaches its stop time. We + // can't tell the difference as there is no indication in the callback. This should + // be okay as we will be timing out this query anyway. + mDNS_Lock(m); + if (question->TimeoutQuestion) + { + if ((m->timenow - question->StopTime) >= 0) { - mDNS_RenameAndReregisterService(gmDNS, srs, mDNSNULL); - return; + LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); + error = kDNSServiceErr_Timeout; } - else + } + mDNS_Unlock(m); + // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft + // Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative + // answers for just about every single multicast name we ever look up, since the Microsoft Active Directory + // server is going to assert that pretty much every single multicast name doesn't exist. + // + // If we are timing out this query, we need to deliver the negative answer to the application + if (error != kDNSServiceErr_Timeout) + { + if (!answer->InterfaceID && IsLocalDomain(answer->name)) { - 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) + // Sanity check: "q" will be set only if "question" is the .local unicast query. + if (!q) { - abort_request(rs); - unlink_request(rs); + LogMsg("queryrecord_result_callback: ERROR!! answering multicast question %s with unicast cache record", + RRDisplayString(m, answer)); + return; } - return; +#if APPLE_OSX_mDNSResponder + if (!ShouldDeliverNegativeResponse(m, question)) + { + return; + } +#endif // APPLE_OSX_mDNSResponder + LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with negative unicast response", question->qname.c, + DNSTypeName(question->qtype)); } - } - else + error = kDNSServiceErr_NoSuchRecord; + } + } + // If we get a negative answer, try appending search domains. Don't append search domains + // - if we are timing out this question + // - if the negative response was received as a result of a multicast query + // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below) + // - if this response is forced e.g., dnssec validation result + if (error != kDNSServiceErr_Timeout) + { + if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord && AddRecord != QC_dnssec) { - 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) + // If the original question did not end in .local, we did not send an SOA query + // to figure out whether we should send an additional unicast query or not. If we just + // appended .local, we need to see if we need to send an additional query. This should + // normally happen just once because after we append .local, we ignore all negative + // responses for .local above. + LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype)); + if (RetryQuestionWithSearchDomains(m, question, req, AddRecord)) { - abort_request(rs); - unlink_request(rs); + // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could + // be anywhere in the search domain list. +#if APPLE_OSX_mDNSResponder + mStatus err = mStatus_NoError; + err = SendAdditionalQuery(question, req, err); + if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains"); +#endif // APPLE_OSX_mDNSResponder + return; } - return; } } + queryrecord_result_reply(m, req, question, answer, AddRecord, error); +} + +mDNSlocal void queryrecord_termination_callback(request_state *request) +{ + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP PID[%d](%s)", + request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), request->process_id, request->pid_name); + if (request->u.queryrecord.q.QuestionContext) + { + mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check + LogMcastQ(&mDNSStorage, &request->u.queryrecord.q, request, q_stop); + request->u.queryrecord.q.QuestionContext = mDNSNULL; + } + else + { + DNSQuestion *question = &request->u.queryrecord.q; + LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); + } -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; + if (request->u.queryrecord.q.qnameOrig) + { + freeL("QueryTermination", request->u.queryrecord.q.qnameOrig); + request->u.queryrecord.q.qnameOrig = mDNSNULL; + } + + if (callExternalHelpers(request->u.queryrecord.q.InterfaceID, &request->u.queryrecord.q.qname, request->flags)) + { + LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()"); + external_stop_browsing_for_service(request->u.queryrecord.q.InterfaceID, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype, request->flags); + } + if (request->u.queryrecord.q2) + { + if (request->u.queryrecord.q2->QuestionContext) + { + LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c); + mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2); + LogMcastQ(&mDNSStorage, request->u.queryrecord.q2, request, q_stop); } - - 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; } + else + { + DNSQuestion *question = request->u.queryrecord.q2; + LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID); + } + if (request->u.queryrecord.q2->qnameOrig) + { + LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c); + freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig); + request->u.queryrecord.q2->qnameOrig = mDNSNULL; + } + freeL("queryrecord Q2", request->u.queryrecord.q2); + request->u.queryrecord.q2 = mDNSNULL; + } +#if APPLE_OSX_mDNSResponder + { + if (request->u.queryrecord.ans) + { + DNSQuestion *v4q, *v6q; + // If we are receiving poisitive answers, provide the hint to the + // upper layer. + v4q = v6q = mDNSNULL; + if (request->u.queryrecord.q.qtype == kDNSType_A) + v4q = &request->u.queryrecord.q; + else if (request->u.queryrecord.q.qtype == kDNSType_AAAA) + v6q = &request->u.queryrecord.q; + mDNSPlatformTriggerDNSRetry(&mDNSStorage, v4q, v6q); + } + } +#endif // APPLE_OSX_mDNSResponder +} + +mDNSlocal void SetQuestionPolicy(DNSQuestion *q, request_state *req) +{ + int i; + + // The policy is either based on pid or UUID. Pass a zero pid + // to the "core" if the UUID is valid. If we always pass the pid, + // then the "core" needs to determine whether the uuid is valid + // by examining all the 16 bytes at the time of the policy + // check and also when setting the delegate socket option. Also, it + // requires that we zero out the uuid wherever the question is + // initialized to make sure that it is not interpreted as valid. + // To prevent these intrusive changes, just pass a zero pid to indicate + // that pid is not valid when uuid is valid. In future if we need the + // pid in the question, we will reevaluate this strategy. + if (req->validUUID) + { + for (i = 0; i < UUID_SIZE; i++) + { + q->uuid[i] = req->uuid[i]; + } + q->pid = 0; + } + else + { + q->pid = req->process_id; + } +} - extra->ClientID = rstate->hdr.reg_index; - return result; - } +mDNSlocal mStatus handle_queryrecord_request(request_state *request) +{ + DNSQuestion *const q = &request->u.queryrecord.q; + char name[256]; + mDNSu16 rrtype, rrclass; + mStatus err; -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); + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - 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); + // The request is scoped to a specific interface index, but the + // interface is not currently in our list. + if (interfaceIndex && !InterfaceID) + { + // If it's one of the specially defined inteface index values, just return an error. + if (PreDefinedInterfaceIndex(interfaceIndex)) + { + LogMsg("ERROR: handle_queryrecord_request: bad interfaceIndex %d", interfaceIndex); + return(mStatus_BadParamErr); + } - // 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; } + // Otherwise, use the specified interface index value and the request will + // be applied to that interface when it comes up. + InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; + LogInfo("handle_queryrecord_request: query pending for interface index %d", interfaceIndex); + } - 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; - } + if (get_string(&request->msgptr, request->msgend, name, 256) < 0) return(mStatus_BadParamErr); + rrtype = get_uint16(&request->msgptr, request->msgend); + rrclass = get_uint16(&request->msgptr, request->msgend); -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 - } + if (!request->msgptr) + { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } -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>"); + request->flags = flags; + mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord)); - return(result); + q->InterfaceID = InterfaceID; + q->flags = flags; + q->Target = zeroAddr; + if (!MakeDomainNameFromDNSNameString(&q->qname, name)) return(mStatus_BadParamErr); +#if 0 + if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains)) return (mStatus_NoError); +#endif + q->qtype = rrtype; + q->qclass = rrclass; + q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; + q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; + q->WakeOnResolve = 0; + q->UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + q->DenyOnCellInterface = (flags & kDNSServiceFlagsDenyCellular) != 0; + q->DenyOnExpInterface = (flags & kDNSServiceFlagsDenyExpensive) != 0; + if ((flags & kDNSServiceFlagsValidate) != 0) + q->ValidationRequired = DNSSEC_VALIDATION_SECURE; + else if ((flags & kDNSServiceFlagsValidateOptional) != 0) + q->ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL; + q->ValidatingResponse = 0; + q->ProxyQuestion = 0; + q->AnonInfo = mDNSNULL; + q->QuestionCallback = queryrecord_result_callback; + q->QuestionContext = request; + q->SearchListIndex = 0; + + q->DNSSECAuthInfo = mDNSNULL; + q->DAIFreeCallback = mDNSNULL; + + //Turn off dnssec validation for local domains and Question Types: RRSIG/ANY(ANY Type is not supported yet) + if ((IsLocalDomain(&q->qname)) || (q->qtype == kDNSServiceType_RRSIG) || (q->qtype == kDNSServiceType_ANY)) + q->ValidationRequired = 0; + + // Don't append search domains for fully qualified domain names including queries + // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally + // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should + // append search domains or not. So, we record that information in AppendSearchDomains. + // + // We append search domains only for queries that are a single label. If overriden using command line + // argument "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified. + // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set. + + if ((!(q->ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(q->ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && (rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' && + (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) + { + q->AppendSearchDomains = 1; + q->AppendLocalSearchDomains = 1; } - -mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd) + else { - (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) + q->AppendSearchDomains = 0; + q->AppendLocalSearchDomains = 0; + } + + // For single label queries that are not fully qualified, look at /etc/hosts, cache and try + // search domains before trying them on the wire as a single label query. RetryWithSearchDomains + // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or + // the cache + q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; + q->qnameOrig = mDNSNULL; + SetQuestionPolicy(q, request); + + LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START PID[%d](%s)", + request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), request->process_id, request->pid_name); + err = mDNS_StartQuery(&mDNSStorage, q); + + if (err) + LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err); + else { - AuthRecord *rr; - registered_record_entry *re; - mStatus result; - - if (rstate->ts != t_complete) + request->terminate = queryrecord_termination_callback; + LogMcastQ(&mDNSStorage, q, request, q_start); + if (callExternalHelpers(q->InterfaceID, &q->qname, flags)) { - LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete"); - abort_request(rstate); - unlink_request(rstate); - return(-1); + LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()"); + external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, flags); } - - 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; +#if APPLE_OSX_mDNSResponder + err = SendAdditionalQuery(q, request, err); +#endif // APPLE_OSX_mDNSResponder + + return(err); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNSServiceEnumerateDomains +#endif + +mDNSlocal reply_state *format_enumeration_reply(request_state *request, + const char *domain, DNSServiceFlags flags, mDNSu32 ifi, DNSServiceErrorType err) +{ + size_t len; reply_state *reply; - transfer_state ts; - (void)m; // Unused + char *data; - 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(mDNSu32); 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; + len += strlen(domain) + 1; - ptr = rstate->msgdata; - get_flags(&ptr); // flags unused + reply = create_reply(enumeration_reply_op, len, request); + reply->rhdr->flags = dnssd_htonl(flags); + reply->rhdr->ifi = dnssd_htonl(ifi); + reply->rhdr->error = dnssd_htonl(err); + data = (char *)&reply->rhdr[1]; + put_string(domain, &data); + return reply; +} - if (rstate->reg_recs) err = remove_record(rstate); // remove individually registered record - else if (!srvinfo) LogOperation("%3d: DNSServiceRemoveRecord (bad ref)", rstate->sd); +mDNSlocal void enum_termination_callback(request_state *request) +{ + // Stop the domain enumeration queries to discover the WAB Browse/Registration domains + if (request->u.enumeration.flags & kDNSServiceFlagsRegistrationDomains) + { + LogInfo("%3d: DNSServiceEnumeration Cancel WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY); + } 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); + { + LogInfo("%3d: DNSServiceEnumeration Cancel WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY); } + mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); + mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default); +} -// 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) +mDNSlocal void enum_result_callback(mDNS *const m, + DNSQuestion *const question, const ResourceRecord *const answer, QC_result AddRecord) +{ + char domain[MAX_ESCAPED_DOMAIN_NAME]; + request_state *request = question->QuestionContext; + DNSServiceFlags flags = 0; + reply_state *reply; + (void)m; // Unused + + if (answer->rrtype != kDNSType_PTR) return; + +#if 0 + if (!AuthorizedDomain(request, &answer->rdata->u.name, request->u.enumeration.flags ? AutoRegistrationDomains : AutoBrowseDomains)) return; +#endif + + // We only return add/remove events for the browse and registration lists + // For the default browse and registration answers, we only give an "ADD" event + if (question == &request->u.enumeration.q_default && !AddRecord) return; + + if (AddRecord) { - DNSServiceFlags flags; - uint32_t ifi; - mDNSInterfaceID InterfaceID; - char *ptr = rstate->msgdata; - domain_enum_t *def, *all; - enum_termination_t *term; + flags |= kDNSServiceFlagsAdd; + if (question == &request->u.enumeration.q_default) flags |= kDNSServiceFlagsDefault; + } + + ConvertDomainNameToCString(&answer->rdata->u.name, domain); + // Note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from + // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the + // network, so we just pass kDNSServiceInterfaceIndexAny + reply = format_enumeration_reply(request, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError); + if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); return; } + + LogOperation("%3d: DNSServiceEnumerateDomains(%#2s) RESULT %s: %s", request->sd, question->qname.c, AddRecord ? "Add" : "Rmv", domain); + + append_reply(request, reply); +} + +mDNSlocal mStatus handle_enum_request(request_state *request) +{ mStatus err; - 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 - + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + DNSServiceFlags reg = flags & kDNSServiceFlagsRegistrationDomains; + mDNS_DomainType t_all = reg ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; + mDNS_DomainType t_default = reg ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault; + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); + + if (!request->msgptr) + { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + + // mark which kind of enumeration we're doing so that we know what domain enumeration queries to stop + request->u.enumeration.flags = reg; + // 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; - + request->u.enumeration.q_all.QuestionContext = request; + request->u.enumeration.q_default.QuestionContext = request; + + // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list. + if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly; + // make the calls - LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", 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) + LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", request->sd, flags, + (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" : + (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>"); + err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_all, t_all, NULL, InterfaceID, enum_result_callback, request); + if (!err) + { + err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_default, t_default, NULL, InterfaceID, enum_result_callback, request); + if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); + else request->terminate = enum_termination_callback; + } + if (!err) + { + // Start the domain enumeration queries to discover the WAB Browse/Registration domains + if (reg) { - abort_request(rstate); - unlink_request(rstate); - return; + LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY); + } + else + { + LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY); } } -mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + return(err); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNSServiceReconfirmRecord & Misc +#endif + +mDNSlocal mStatus handle_reconfirm_request(request_state *request) +{ + mStatus status = mStatus_BadParamErr; + AuthRecord *rr = read_rr_from_ipc_msg(request, 0, 0); + if (rr) { - char domain[MAX_ESCAPED_DOMAIN_NAME]; - domain_enum_t *de = question->QuestionContext; - DNSServiceFlags flags = 0; - reply_state *reply; - (void)m; // Unused + status = mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec); + LogOperation( + (status == mStatus_NoError) ? + "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" : + "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d", + request->sd, RRDisplayString(&mDNSStorage, &rr->resrec), + mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status); + freeL("AuthRecord/handle_reconfirm_request", rr); + } + return(status); +} - 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; +#if APPLE_OSX_mDNSResponder + +mDNSlocal mStatus handle_release_request(request_state *request) +{ + mStatus err = 0; + char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; + domainname instance; + + // extract the data from the message + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + + if (get_string(&request->msgptr, request->msgend, name, 256) < 0 || + get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) + { + LogMsg("ERROR: handle_release_request - Couldn't read name/regtype/domain"); + return(mStatus_BadParamErr); } -mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err) + if (!request->msgptr) { - size_t len; - reply_state *reply; - char *data; + LogMsg("%3d: PeerConnectionRelease(unreadable parameters)", request->sd); + return(mStatus_BadParamErr); + } - len = sizeof(DNSServiceFlags); - len += sizeof(uint32_t); - len += sizeof(DNSServiceErrorType); - len += strlen(domain) + 1; + if (build_domainname_from_strings(&instance, name, regtype, domain) < 0) + { + LogMsg("ERROR: handle_release_request bad “%s” “%s” “%s”", name, regtype, domain); + return(mStatus_BadParamErr); + } - 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; + LogOperation("%3d: PeerConnectionRelease(%X %##s) START PID[%d](%s)", + request->sd, flags, instance.c, request->process_id, request->pid_name); + + external_connection_release(&instance); + return(err); +} + +#else // APPLE_OSX_mDNSResponder + +mDNSlocal mStatus handle_release_request(request_state *request) +{ + (void) request; + return mStatus_UnsupportedErr; +} + +#endif // APPLE_OSX_mDNSResponder + +mDNSlocal mStatus handle_setdomain_request(request_state *request) +{ + char domainstr[MAX_ESCAPED_DOMAIN_NAME]; + domainname domain; + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + (void)flags; // Unused + if (get_string(&request->msgptr, request->msgend, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 || + !MakeDomainNameFromDNSNameString(&domain, domainstr)) + { LogMsg("%3d: DNSServiceSetDefaultDomainForUser(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + + LogOperation("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c); + return(mStatus_NoError); +} + +typedef packedstruct +{ + mStatus err; + mDNSu32 len; + mDNSu32 vers; +} DaemonVersionReply; + +mDNSlocal void handle_getproperty_request(request_state *request) +{ + const mStatus BadParamErr = dnssd_htonl((mDNSu32)mStatus_BadParamErr); + char prop[256]; + if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0) + { + LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop); + if (!strcmp(prop, kDNSServiceProperty_DaemonVersion)) + { + DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) }; + send_all(request->sd, (const char *)&x, sizeof(x)); + return; + } } -mDNSlocal void enum_termination_callback(void *context) + // If we didn't recogize the requested property name, return BadParamErr + send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr)); +} + +#ifdef APPLE_OSX_mDNSResponder +// The caller can specify either the pid or the uuid. If the pid is not specified, +// update the effective uuid. Don't overwrite the pid which is used for debugging +// purposes and initialized when the socket is opened. +mDNSlocal void handle_connection_delegate_request(request_state *request) +{ + mDNSs32 pid; + socklen_t len; + + len = 0; + pid = get_uint32(&request->msgptr, request->msgend); +#ifdef LOCAL_PEEREPID + if (pid) + { + len = sizeof(pid); + if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREPID, &request->process_id, &len) != 0) + return; + // to extract the process name from the pid value + if (proc_pidinfo(request->process_id, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) + return; + mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm); + //LogMsg("handle_connection_delegate_request: process id %d, name %s", request->process_id, request->pid_name); + } +#endif +#ifdef LOCAL_PEEREUUID + if (!pid) { - enum_termination_t *t = context; - mDNS *coredata = gmDNS; + len = UUID_SIZE; + if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREUUID, request->uuid, &len) != 0) + return; + request->validUUID = mDNStrue; + } +#endif +} +#else +mDNSlocal void handle_connection_delegate_request(request_state *request) +{ + (void) request; +} +#endif - 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); +typedef packedstruct +{ + mStatus err; + mDNSs32 pid; +} PIDInfo; + +mDNSlocal void handle_getpid_request(request_state *request) +{ + const request_state *req; + mDNSs32 pid = -1; + mDNSu16 srcport = get_uint16(&request->msgptr, request->msgend); + const DNSQuestion *q = NULL; + PIDInfo pi; + + LogOperation("%3d: DNSServiceGetPID START", request->sd); + + for (req = all_requests; req; req=req->next) + { + if (req->hdr.op == query_request) + q = &req->u.queryrecord.q; + else if (req->hdr.op == addrinfo_request) + q = &req->u.addrinfo.q4; + else if (req->hdr.op == addrinfo_request) + q = &req->u.addrinfo.q6; + + if (q && q->LocalSocket != NULL) + { + mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket); + if (port == srcport) + { + pid = req->process_id; + LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s] question %##s", htons(srcport), pid, req->pid_name, q->qname.c); + break; + } + } } + // If we cannot find in the client requests, look to see if this was + // started by mDNSResponder. + if (pid == -1) + { + for (q = mDNSStorage.Questions; q; q = q->next) + { + if (q && q->LocalSocket != NULL) + { + mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket); + if (port == srcport) + { +#if APPLE_OSX_mDNSResponder + pid = getpid(); +#endif // APPLE_OSX_mDNSResponder + LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s], question %##s", htons(srcport), pid, "_mDNSResponder", q->qname.c); + break; + } + } + } + } + + pi.err = 0; + pi.pid = pid; + send_all(request->sd, (const char *)&pi, sizeof(PIDInfo)); + LogOperation("%3d: DNSServiceGetPID STOP", request->sd); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNSServiceNATPortMappingCreate +#endif + +#define DNSServiceProtocol(X) ((X) == NATOp_AddrRequest ? 0 : (X) == NATOp_MapUDP ? kDNSServiceProtocol_UDP : kDNSServiceProtocol_TCP) + +mDNSlocal void port_mapping_termination_callback(request_state *request) +{ + LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP PID[%d](%s)", request->sd, + DNSServiceProtocol(request->u.pm.NATinfo.Protocol), + mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, + request->process_id, request->pid_name); + mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo); +} + +// Called via function pointer when we get a NAT Traversal (address request or port mapping) response +mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n) +{ + request_state *request = (request_state *)n->clientContext; + reply_state *rep; + int replyLen; + char *data; + + if (!request) { LogMsg("port_mapping_create_request_callback called with unknown request_state object"); return; } + + // calculate reply data length + replyLen = sizeof(DNSServiceFlags); + replyLen += 3 * sizeof(mDNSu32); // if index + addr + ttl + replyLen += sizeof(DNSServiceErrorType); + replyLen += 2 * sizeof(mDNSu16); // Internal Port + External Port + replyLen += sizeof(mDNSu8); // protocol + + rep = create_reply(port_mapping_reply_op, replyLen, request); -mDNSlocal void handle_reconfirm_request(request_state *rstate) + rep->rhdr->flags = dnssd_htonl(0); + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse)); + rep->rhdr->error = dnssd_htonl(n->Result); + + data = (char *)&rep->rhdr[1]; + + *data++ = request->u.pm.NATinfo.ExternalAddress.b[0]; + *data++ = request->u.pm.NATinfo.ExternalAddress.b[1]; + *data++ = request->u.pm.NATinfo.ExternalAddress.b[2]; + *data++ = request->u.pm.NATinfo.ExternalAddress.b[3]; + *data++ = DNSServiceProtocol(request->u.pm.NATinfo.Protocol); + *data++ = request->u.pm.NATinfo.IntPort.b[0]; + *data++ = request->u.pm.NATinfo.IntPort.b[1]; + *data++ = request->u.pm.NATinfo.ExternalPort.b[0]; + *data++ = request->u.pm.NATinfo.ExternalPort.b[1]; + put_uint32(request->u.pm.NATinfo.Lifetime, &data); + + LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT %.4a:%u TTL %u", request->sd, + DNSServiceProtocol(request->u.pm.NATinfo.Protocol), + mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, + &request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), request->u.pm.NATinfo.Lifetime); + + append_reply(request, rep); +} + +mDNSlocal mStatus handle_port_mapping_request(request_state *request) +{ + mDNSu32 ttl = 0; + mStatus err = mStatus_NoError; + + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + mDNSu8 protocol = (mDNSu8)get_uint32(&request->msgptr, request->msgend); + (void)flags; // Unused + if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); + if (request->msgptr + 8 > request->msgend) request->msgptr = NULL; + else { - 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; + request->u.pm.NATinfo.IntPort.b[0] = *request->msgptr++; + request->u.pm.NATinfo.IntPort.b[1] = *request->msgptr++; + request->u.pm.ReqExt.b[0] = *request->msgptr++; + request->u.pm.ReqExt.b[1] = *request->msgptr++; + ttl = get_uint32(&request->msgptr, request->msgend); + } + + if (!request->msgptr) + { LogMsg("%3d: DNSServiceNATPortMappingCreate(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + + if (protocol == 0) // If protocol == 0 (i.e. just request public address) then IntPort, ExtPort, ttl must be zero too + { + if (!mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort) || !mDNSIPPortIsZero(request->u.pm.ReqExt) || ttl) return(mStatus_BadParamErr); + } + else + { + if (mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort)) return(mStatus_BadParamErr); + if (!(protocol & (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP))) return(mStatus_BadParamErr); + } + + request->u.pm.NATinfo.Protocol = !protocol ? NATOp_AddrRequest : (protocol == kDNSServiceProtocol_UDP) ? NATOp_MapUDP : NATOp_MapTCP; + // u.pm.NATinfo.IntPort = already set above + request->u.pm.NATinfo.RequestedPort = request->u.pm.ReqExt; + request->u.pm.NATinfo.NATLease = ttl; + request->u.pm.NATinfo.clientCallback = port_mapping_create_request_callback; + request->u.pm.NATinfo.clientContext = request; + + LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START PID[%d](%s)", request->sd, + protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, + request->process_id, request->pid_name); + err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo); + if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err); + else request->terminate = port_mapping_termination_callback; + + return(err); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNSServiceGetAddrInfo +#endif + +mDNSlocal void addrinfo_termination_callback(request_state *request) +{ + LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP PID[%d](%s)", request->sd, request->u.addrinfo.q4.qname.c, + request->process_id, request->pid_name); + + if (request->u.addrinfo.q4.QuestionContext) + { + mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); + LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_stop); + request->u.addrinfo.q4.QuestionContext = mDNSNULL; + } + if (request->u.addrinfo.q4.qnameOrig) + { + freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig); + request->u.addrinfo.q4.qnameOrig = mDNSNULL; + } + if (request->u.addrinfo.q42) + { + if (request->u.addrinfo.q42->QuestionContext) + { + LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c); + mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42); + LogMcastQ(&mDNSStorage, request->u.addrinfo.q42, request, q_stop); } - type = get_short(&msgbuf); - class = get_short(&msgbuf); - rdlen = get_short(&msgbuf); + if (request->u.addrinfo.q42->qnameOrig) + { + LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c); + freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig); + request->u.addrinfo.q42->qnameOrig = mDNSNULL; + } + freeL("addrinfo Q42", request->u.addrinfo.q42); + request->u.addrinfo.q42 = mDNSNULL; + } - if (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 (request->u.addrinfo.q6.QuestionContext) + { + mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); + LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_stop); + request->u.addrinfo.q6.QuestionContext = mDNSNULL; + } + if (request->u.addrinfo.q6.qnameOrig) + { + freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig); + request->u.addrinfo.q6.qnameOrig = mDNSNULL; + } + if (request->u.addrinfo.q62) + { + if (request->u.addrinfo.q62->QuestionContext) + { + LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c); + mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62); + LogMcastQ(&mDNSStorage, request->u.addrinfo.q62, request, q_stop); + } + if (request->u.addrinfo.q62->qnameOrig) + { + LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c); + freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig); + request->u.addrinfo.q62->qnameOrig = mDNSNULL; + } + freeL("addrinfo Q62", request->u.addrinfo.q62); + request->u.addrinfo.q62 = mDNSNULL; + } +#if APPLE_OSX_mDNSResponder + { + DNSQuestion *v4q, *v6q; + v4q = v6q = mDNSNULL; + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) + { + // If we are not delivering answers, we may be timing out prematurely. + // Note down the current state so that we know to retry when we see a + // valid response again. + if (request->u.addrinfo.q4.TimeoutQuestion && !request->u.addrinfo.v4ans) + { + mDNSPlatformUpdateDNSStatus(&mDNSStorage, &request->u.addrinfo.q4); + } + // If we have a v4 answer and if we timed out prematurely before, provide + // a trigger to the upper layer so that it can retry questions if needed. + if (request->u.addrinfo.v4ans) + v4q = &request->u.addrinfo.q4; + } + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) + { + if (request->u.addrinfo.q6.TimeoutQuestion && !request->u.addrinfo.v6ans) + { + mDNSPlatformUpdateDNSStatus(&mDNSStorage, &request->u.addrinfo.q6); + } + if (request->u.addrinfo.v6ans) + v6q = &request->u.addrinfo.q6; + } + mDNSPlatformTriggerDNSRetry(&mDNSStorage, v4q, v6q); + } +#endif // APPLE_OSX_mDNSResponder +} + +mDNSlocal mStatus handle_addrinfo_request(request_state *request) +{ + char hostname[256]; + domainname d; + mStatus err = 0; + mDNSs32 serviceIndex = -1; // default unscoped value for ServiceID is -1 - if (!MakeDomainNameFromDNSNameString(rr->resrec.name, name)) - { - LogMsg("ERROR: bad name: %s", name); - freeL("read_rr_from_ipc_msg", rr); - return NULL; - } + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - 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; + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); + + if (flags & kDNSServiceFlagsServiceIndex) + { + // NOTE: kDNSServiceFlagsServiceIndex flag can only be set for DNSServiceGetAddrInfo() + LogInfo("DNSServiceGetAddrInfo: kDNSServiceFlagsServiceIndex is SET by the client"); + // if kDNSServiceFlagsServiceIndex is SET, + // interpret the interfaceID as the serviceId and set the interfaceID to 0. + serviceIndex = interfaceIndex; + interfaceIndex = 0; } + + mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo)); -mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain) + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + + // The request is scoped to a specific interface index, but the + // interface is not currently in our list. + if (interfaceIndex && !InterfaceID) { - domainlabel n; - domainname d, t; + // If it's one of the specially defined inteface index values, just return an error. + if (PreDefinedInterfaceIndex(interfaceIndex)) + { + LogMsg("ERROR: handle_addrinfo_request: bad interfaceIndex %d", interfaceIndex); + return(mStatus_BadParamErr); + } - 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; + // Otherwise, use the specified interface index value and the registration will + // be applied to that interface when it comes up. + InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; + LogInfo("handle_addrinfo_request: query pending for interface index %d", interfaceIndex); } + request->u.addrinfo.interface_id = InterfaceID; + request->u.addrinfo.flags = flags; + request->u.addrinfo.protocol = get_uint32(&request->msgptr, request->msgend); -// append a reply to the list in a request object -mDNSlocal void append_reply(request_state *req, reply_state *rep) + if (request->u.addrinfo.protocol > (kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) return(mStatus_BadParamErr); + + if (get_string(&request->msgptr, request->msgend, hostname, 256) < 0) return(mStatus_BadParamErr); + + if (!request->msgptr) { LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + + if (!MakeDomainNameFromDNSNameString(&d, hostname)) + { LogMsg("ERROR: handle_addrinfo_request: bad hostname: %s", hostname); return(mStatus_BadParamErr); } + +#if 0 + if (!AuthorizedDomain(request, &d, AutoBrowseDomains)) return (mStatus_NoError); +#endif + + if (!request->u.addrinfo.protocol) { - reply_state *ptr; + flags |= kDNSServiceFlagsSuppressUnusable; + request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); + } - if (!req->replies) req->replies = rep; - else - { - ptr = req->replies; - while (ptr->next) ptr = ptr->next; - ptr->next = rep; - } - rep->next = NULL; + request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id; + request->u.addrinfo.q4.ServiceID = request->u.addrinfo.q6.ServiceID = serviceIndex; + request->u.addrinfo.q4.flags = request->u.addrinfo.q6.flags = flags; + request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr; + request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d; + request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN; + request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse; + request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; + request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; + request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0; + request->u.addrinfo.q4.UseBackgroundTrafficClass = request->u.addrinfo.q6.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + request->u.addrinfo.q4.DenyOnCellInterface = request->u.addrinfo.q6.DenyOnCellInterface = (flags & kDNSServiceFlagsDenyCellular) != 0; + request->u.addrinfo.q4.DenyOnExpInterface = request->u.addrinfo.q6.DenyOnExpInterface = (flags & kDNSServiceFlagsDenyExpensive) != 0; + if ((flags & kDNSServiceFlagsValidate) != 0) + request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE; + else if ((flags & kDNSServiceFlagsValidateOptional) != 0) + request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL; + request->u.addrinfo.q4.ValidatingResponse = request->u.addrinfo.q6.ValidatingResponse = 0; + request->u.addrinfo.q4.ProxyQuestion = request->u.addrinfo.q6.ProxyQuestion = 0; + request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL; + request->u.addrinfo.q4.AnonInfo = request->u.addrinfo.q6.AnonInfo = mDNSNULL; + + SetQuestionPolicy(&request->u.addrinfo.q4, request); + SetQuestionPolicy(&request->u.addrinfo.q6, request); + + request->u.addrinfo.q4.DNSSECAuthInfo = request->u.addrinfo.q6.DNSSECAuthInfo = mDNSNULL; + request->u.addrinfo.q4.DAIFreeCallback = request->u.addrinfo.q6.DAIFreeCallback = mDNSNULL; + + //Turn off dnssec validation for local domains + if (IsLocalDomain(&d)) + request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = 0; + + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) + { + request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA; + request->u.addrinfo.q6.SearchListIndex = 0; + // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set + if ((!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) + { + request->u.addrinfo.q6.AppendSearchDomains = 1; + request->u.addrinfo.q6.AppendLocalSearchDomains = 1; + } + else + { + request->u.addrinfo.q6.AppendSearchDomains = 0; + request->u.addrinfo.q6.AppendLocalSearchDomains = 0; + } + request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0); + request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback; + request->u.addrinfo.q6.QuestionContext = request; + err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6); + if (err != mStatus_NoError) + { + LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); + request->u.addrinfo.q6.QuestionContext = mDNSNULL; + } + #if APPLE_OSX_mDNSResponder + err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err); + #endif // APPLE_OSX_mDNSResponder + if (!err) + { + request->terminate = addrinfo_termination_callback; + LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_start); + } } -// 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) + if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)) { - uint32_t nleft; - int nread; - char buf[4]; // dummy for death notification - - if (rs->ts == t_terminated || rs->ts == t_error) + request->u.addrinfo.q4.qtype = kDNSServiceType_A; + request->u.addrinfo.q4.SearchListIndex = 0; + + // We append search domains only for queries that are a single label. If overriden using cmd line arg + // "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified. + // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set. + + if ((!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) { - LogMsg("ERROR: read_msg called with transfer state terminated or error"); - rs->ts = t_error; - return t_error; + request->u.addrinfo.q4.AppendSearchDomains = 1; + request->u.addrinfo.q4.AppendLocalSearchDomains = 1; } - - 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; } + else + { + request->u.addrinfo.q4.AppendSearchDomains = 0; + request->u.addrinfo.q4.AppendLocalSearchDomains = 0; + } + request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0); + request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback; + request->u.addrinfo.q4.QuestionContext = request; + err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4); + if (err != mStatus_NoError) + { + LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); + request->u.addrinfo.q4.QuestionContext = mDNSNULL; + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) + { + // If we started a query for IPv6, we need to cancel it + mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); + request->u.addrinfo.q6.QuestionContext = mDNSNULL; + } + } + #if APPLE_OSX_mDNSResponder + err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err); + #endif // APPLE_OSX_mDNSResponder + if (!err) + { + request->terminate = addrinfo_termination_callback; + LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_start); + } + } + + LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, + request->u.addrinfo.protocol, d.c, request->process_id, request->pid_name); + return(err); +} + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Main Request Handler etc. +#endif + +mDNSlocal request_state *NewRequest(void) +{ + request_state **p = &all_requests; + while (*p) + p=&(*p)->next; + *p = mallocL("request_state", sizeof(request_state)); + if (!*p) + FatalError("ERROR: malloc"); + mDNSPlatformMemZero(*p, sizeof(request_state)); + return(*p); +} + +// read_msg may be called any time when the transfer state (req->ts) is t_morecoming. +// if there is no data on the socket, the socket will be closed and t_terminated will be returned +mDNSlocal void read_msg(request_state *req) +{ + if (req->ts == t_terminated || req->ts == t_error) + { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; } + + if (req->ts == t_complete) // this must be death or something is wrong + { + char buf[4]; // dummy for death notification + int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data); + if (!nread) { req->ts = t_terminated; return; } + if (nread < 0) goto rerror; + LogMsg("%3d: ERROR: read data from a completed request", req->sd); + req->ts = t_error; + return; + } + + if (req->ts != t_morecoming) + { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; } + + if (req->hdr_bytes < sizeof(ipc_msg_hdr)) + { + mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes; + int nread = udsSupportReadFD(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0, req->platform_data); + if (nread == 0) { req->ts = t_terminated; return; } if (nread < 0) goto rerror; - LogMsg("ERROR: read data from a completed request."); - rs->ts = t_error; - return t_error; - } + req->hdr_bytes += nread; + if (req->hdr_bytes > sizeof(ipc_msg_hdr)) + { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; } - if (rs->ts != t_morecoming) + // only read data if header is complete + if (req->hdr_bytes == sizeof(ipc_msg_hdr)) { - LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs->ts); - rs->ts = t_error; - return t_error; + ConvertHeaderBytes(&req->hdr); + if (req->hdr.version != VERSION) + { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; } + + // Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord() + // with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin + // for other overhead, this means any message above 70kB is definitely bogus. + if (req->hdr.datalen > 70000) + { LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; } + req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES); + if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; } + req->msgptr = req->msgbuf; + req->msgend = req->msgbuf + req->hdr.datalen; + mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES); } - - if (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 our header is complete, but we're still needing more body data, then try to read it now + // Note: For cancel_request req->hdr.datalen == 0, but there's no error return socket for cancel_request + // Any time we need to get the error return socket we know we'll have at least one data byte + // (even if only the one-byte empty C string placeholder for the old ctrl_path parameter) + if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen) + { + mDNSu32 nleft = req->hdr.datalen - req->data_bytes; + int nread; +#if !defined(_WIN32) + struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put + struct msghdr msg; + struct cmsghdr *cmsg; + char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))]; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + msg.msg_flags = 0; + nread = recvmsg(req->sd, &msg, 0); +#else + nread = udsSupportReadFD(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0, req->platform_data); +#endif + if (nread == 0) { req->ts = t_terminated; return; } if (nread < 0) goto rerror; - 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)) + req->data_bytes += nread; + if (req->data_bytes > req->hdr.datalen) + { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; } +#if !defined(_WIN32) + cmsg = CMSG_FIRSTHDR(&msg); +#if DEBUG_64BIT_SCM_RIGHTS + LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); + LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type); +#endif // DEBUG_64BIT_SCM_RIGHTS + if (msg.msg_controllen != 0 && + cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) + { +#if APPLE_OSX_mDNSResponder + // Strictly speaking BPF_fd belongs solely in the platform support layer, but because + // of privilege separation on Mac OS X we need to get BPF_fd from mDNSResponderHelper, + // and it's convenient to repurpose the existing fd-passing code here for that task + if (req->hdr.op == send_bpf) + { + dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg); + LogOperation("%3d: Got len %d, BPF %d", req->sd, cmsg->cmsg_len, x); + mDNSPlatformReceiveBPF_fd(&mDNSStorage, x); + } + else +#endif // APPLE_OSX_mDNSResponder + req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg); +#if DEBUG_64BIT_SCM_RIGHTS + LogMsg("%3d: read req->errsd %d", req->sd, req->errsd); +#endif // DEBUG_64BIT_SCM_RIGHTS + if (req->data_bytes < req->hdr.datalen) { - LogMsg("ERROR: read_msg - read too many header bytes"); - rs->ts = t_error; - return t_error; + LogMsg("%3d: Client(PID [%d](%s)) sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d", + req->sd, req->process_id, req->pid_name, req->errsd, req->data_bytes, req->hdr.datalen); + req->ts = t_error; + return; } - } + } +#endif + } - // only read data if header is complete - if (rs->hdr_bytes == sizeof(ipc_msg_hdr)) - { - if (rs->hdr.datalen == 0) // ok in removerecord requests + // If our header and data are both complete, see if we need to make our separate error return socket + if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes == req->hdr.datalen) + { + if (req->terminate && req->hdr.op != cancel_request) + { + dnssd_sockaddr_t cliaddr; +#if defined(USE_TCP_LOOPBACK) + mDNSOpaque16 port; + u_long opt = 1; + port.b[0] = req->msgptr[0]; + port.b[1] = req->msgptr[1]; + req->msgptr += 2; + cliaddr.sin_family = AF_INET; + cliaddr.sin_port = port.NotAnInteger; + cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); +#else + char ctrl_path[MAX_CTLPATH]; + get_string(&req->msgptr, req->msgend, ctrl_path, MAX_CTLPATH); // path is first element in message buffer + mDNSPlatformMemZero(&cliaddr, sizeof(cliaddr)); + cliaddr.sun_family = AF_LOCAL; + mDNSPlatformStrCopy(cliaddr.sun_path, ctrl_path); + // If the error return path UDS name is empty string, that tells us + // that this is a new version of the library that's going to pass us + // the error return path socket via sendmsg/recvmsg + if (ctrl_path[0] == 0) { - rs->ts = t_complete; - rs->msgbuf = NULL; - return t_complete; + if (req->errsd == req->sd) + { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; } + goto got_errfd; } - - if (!rs->msgbuf) // allocate the buffer first time through +#endif + + req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (!dnssd_SocketValid(req->errsd)) + { + my_throttled_perror("ERROR: socket"); + req->ts = t_error; + return; + } + + if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) { - 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); +#if !defined(USE_TCP_LOOPBACK) + struct stat sb; + LogMsg("%3d: read_msg: Couldn't connect to error return path socket “%s” errno %d (%s)", + req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); + if (stat(cliaddr.sun_path, &sb) < 0) + LogMsg("%3d: read_msg: stat failed “%s” errno %d (%s)", req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno)); + else + LogMsg("%3d: read_msg: file “%s” mode %o (octal) uid %d gid %d", req->sd, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid); +#endif + req->ts = t_error; + return; } - 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) + +#if !defined(USE_TCP_LOOPBACK) +got_errfd: +#endif + LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]); +#if defined(_WIN32) + if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0) +#else + if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0) +#endif { - LogMsg("ERROR: read_msg - read too many data bytes"); - rs->ts = t_error; - return t_error; + LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d (%s)", + req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + req->ts = t_error; + return; } } - if (rs->hdr_bytes == sizeof(ipc_msg_hdr) && rs->data_bytes == rs->hdr.datalen) - rs->ts = t_complete; - else rs->ts = t_morecoming; + req->ts = t_complete; + } - return rs->ts; + return; 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) + if (dnssd_errno == dnssd_EWOULDBLOCK || dnssd_errno == dnssd_EINTR) return; + LogMsg("%3d: ERROR: read_msg errno %d (%s)", req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + req->ts = t_error; +} + +#define RecordOrientedOp(X) \ + ((X) == reg_record_request || (X) == add_record_request || (X) == update_record_request || (X) == remove_record_request) + +// The lightweight operations are the ones that don't need a dedicated request_state structure allocated for them +#define LightweightOp(X) (RecordOrientedOp(X) || (X) == cancel_request) + +mDNSlocal void request_callback(int fd, short filter, void *info) +{ + mStatus err = 0; + request_state *req = info; + mDNSs32 min_size = sizeof(DNSServiceFlags); + (void)fd; // Unused + (void)filter; // Unused + + for (;;) { - ssize_t nwriten; - - if (!rs->msgbuf) + read_msg(req); + if (req->ts == t_morecoming) + return; + if (req->ts == t_terminated || req->ts == t_error) + { + AbortUnlinkAndFree(req); + return; + } + if (req->ts != t_complete) + { + LogMsg("request_callback: req->ts %d != t_complete PID[%d][%s]", req->ts, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; + } + if (req->hdr.version != VERSION) { - LogMsg("ERROR: send_msg called with NULL message buffer"); - return t_error; + LogMsg("request_callback: ERROR: client IPC version %d incompatible with daemon IPC version %d PID[%d][%s]", + req->hdr.version, VERSION, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; } - - if (rs->request->no_reply) //!!!KRS this behavior should be optimized if it becomes more common + + switch(req->hdr.op) // Interface + other data { - rs->ts = t_complete; - freeL("send_msg", rs->msgbuf); - return t_complete; + case connection_request: min_size = 0; break; + case connection_delegate_request: min_size = 4; /* pid */ break; + case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break; + case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break; + case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break; + case remove_record_request: break; + case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break; + case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; + case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break; + case enumeration_request: min_size += sizeof(mDNSu32); break; + case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break; + case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break; + case setdomain_request: min_size += 1 /* domain */; break; + case getproperty_request: min_size = 2; break; + case getpid_request: min_size = 2; break; + case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break; + case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break; + case send_bpf: // Same as cancel_request below + case cancel_request: min_size = 0; break; + case release_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; + default: LogMsg("request_callback: ERROR: validate_message - unsupported req type: %d PID[%d][%s]", + req->hdr.op, req->process_id, req->pid_name); + min_size = -1; break; } - 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 ((mDNSs32)req->data_bytes < min_size) + { + LogMsg("request_callback: Invalid message %d bytes; min for %d is %d PID[%d][%s]", + req->data_bytes, req->hdr.op, min_size, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; + } + if (LightweightOp(req->hdr.op) && !req->terminate) + { + LogMsg("request_callback: Reg/Add/Update/Remove %d require existing connection PID[%d][%s]", + req->hdr.op, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; + } + + // check if client wants silent operation + if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1; + + // If req->terminate is already set, this means this operation is sharing an existing connection + if (req->terminate && !LightweightOp(req->hdr.op)) + { + request_state *newreq = NewRequest(); + newreq->primary = req; + newreq->sd = req->sd; + newreq->errsd = req->errsd; + newreq->uid = req->uid; + newreq->hdr = req->hdr; + newreq->msgbuf = req->msgbuf; + newreq->msgptr = req->msgptr; + newreq->msgend = req->msgend; + // if the parent request is a delegate connection, copy the + // relevant bits + if (req->validUUID) { -#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; - } + int i; + newreq->validUUID = mDNStrue; + for (i = 0; i < UUID_SIZE; i++) + { + newreq->uuid[i] = req->uuid[i]; + } + } else -#endif - { - my_perror("ERROR: send\n"); - rs->ts = t_error; - return t_error; - } + { + if (req->process_id) + { + newreq->process_id = req->process_id; + } + else + { + set_peer_pid(newreq); + } + } + req = newreq; + } + + // If we're shutting down, don't allow new client requests + // We do allow "cancel" and "getproperty" during shutdown + if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request) + { + err = mStatus_ServiceNotRunning; + } + else + { + switch(req->hdr.op) + { + // These are all operations that have their own first-class request_state object + case connection_request: + LogOperation("%3d: DNSServiceCreateConnection START PID[%d](%s)", + req->sd, req->process_id, req->pid_name); + req->terminate = connection_termination; + break; + case connection_delegate_request: + LogOperation("%3d: DNSServiceCreateDelegateConnection START PID[%d](%s)", + req->sd, req->process_id, req->pid_name); + req->terminate = connection_termination; + handle_connection_delegate_request(req); + break; + case resolve_request: err = handle_resolve_request (req); break; + case query_request: err = handle_queryrecord_request (req); break; + case browse_request: err = handle_browse_request (req); break; + case reg_service_request: err = handle_regservice_request (req); break; + case enumeration_request: err = handle_enum_request (req); break; + case reconfirm_record_request: err = handle_reconfirm_request (req); break; + case setdomain_request: err = handle_setdomain_request (req); break; + case getproperty_request: handle_getproperty_request (req); break; + case getpid_request: handle_getpid_request (req); break; + case port_mapping_request: err = handle_port_mapping_request(req); break; + case addrinfo_request: err = handle_addrinfo_request (req); break; + case send_bpf: /* Do nothing for send_bpf */ break; + + // These are all operations that work with an existing request_state object + case reg_record_request: err = handle_regrecord_request (req); break; + case add_record_request: err = handle_add_request (req); break; + case update_record_request: err = handle_update_request (req); break; + case remove_record_request: err = handle_removerecord_request(req); break; + case cancel_request: handle_cancel_request (req); break; + case release_request: err = handle_release_request (req); break; + default: LogMsg("request_callback: %3d:ERROR: Unsupported UDS req:%d PID[%d][%s]", + req->sd, req->hdr.op, req->process_id, req->pid_name); break; + } + } + // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request + if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf); + + // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result) + // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here + if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf && req->hdr.op != getpid_request) + { + const mStatus err_netorder = dnssd_htonl(err); + send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder)); + if (req->errsd != req->sd) + { + LogOperation("%3d: Error socket %d closed %08X %08X (%d)", + req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err); + dnssd_close(req->errsd); + req->errsd = req->sd; + // Also need to reset the parent's errsd, if this is a subordinate operation + if (req->primary) req->primary->errsd = req->primary->sd; } } - rs->nwriten += nwriten; - if (rs->nwriten == rs->len) - { - rs->ts = t_complete; - freeL("send_msg", rs->msgbuf); - } - return rs->ts; + // Reset ready to accept the next req on this pipe + if (req->primary) req = req->primary; + req->ts = t_morecoming; + req->hdr_bytes = 0; + req->data_bytes = 0; + req->msgbuf = mDNSNULL; + req->msgptr = mDNSNULL; + req->msgend = 0; } +} -mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request) - { - reply_state *reply; - int totallen; +mDNSlocal void connect_callback(int fd, short filter, void *info) +{ + dnssd_sockaddr_t cliaddr; + dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr); + dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len); +#if defined(SO_NOSIGPIPE) || defined(_WIN32) + unsigned long optval = 1; +#endif - if ((unsigned)datalen < sizeof(reply_hdr)) + (void)filter; // Unused + (void)info; // Unused + + if (!dnssd_SocketValid(sd)) + { + if (dnssd_errno != dnssd_EWOULDBLOCK) + my_throttled_perror("ERROR: accept"); + return; + } + +#ifdef SO_NOSIGPIPE + // Some environments (e.g. OS X) support turning off SIGPIPE for a socket + if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) + LogMsg("%3d: WARNING: setsockopt - SO_NOSIGPIPE %d (%s)", sd, dnssd_errno, dnssd_strerror(dnssd_errno)); +#endif + +#if defined(_WIN32) + if (ioctlsocket(sd, FIONBIO, &optval) != 0) +#else + if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK) != 0) +#endif + { + my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client"); + dnssd_close(sd); + return; + } + else + { + request_state *request = NewRequest(); + request->ts = t_morecoming; + request->sd = sd; + request->errsd = sd; + set_peer_pid(request); +#if APPLE_OSX_mDNSResponder + struct xucred x; + socklen_t xucredlen = sizeof(x); + if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) request->uid = x.cr_uid; + else my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); + debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups); +#endif // APPLE_OSX_mDNSResponder + LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid); + udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data); + } +} + +mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt) +{ +#if defined(SO_NP_EXTENSIONS) + struct so_np_extensions sonpx; + socklen_t optlen = sizeof(struct so_np_extensions); + sonpx.npx_flags = SONPX_SETOPTSHUT; + sonpx.npx_mask = SONPX_SETOPTSHUT; + if (setsockopt(skt, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, optlen) < 0) + my_perror("WARNING: could not set sockopt - SO_NP_EXTENSIONS"); +#endif +#if defined(_WIN32) + // SEH: do we even need to do this on windows? + // This socket will be given to WSAEventSelect which will automatically set it to non-blocking + u_long opt = 1; + if (ioctlsocket(skt, FIONBIO, &opt) != 0) +#else + if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) != 0) +#endif + { + my_perror("ERROR: could not set listen socket to non-blocking mode"); + return mDNSfalse; + } + + if (listen(skt, LISTENQ) != 0) + { + my_perror("ERROR: could not listen on listen socket"); + return mDNSfalse; + } + + if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL, (void **) NULL)) + { + my_perror("ERROR: could not add listen socket to event loop"); + return mDNSfalse; + } + else + { + LogInfo("%3d: Listening for incoming Unix Domain Socket client requests", skt); + mDNSStorage.uds_listener_skt = skt; + } + return mDNStrue; +} + +mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count) +{ + dnssd_sockaddr_t laddr; + int ret; + mDNSu32 i = 0; + + LogInfo("udsserver_init: %d %d", _DNS_SD_H, mDNSStorage.mDNS_plat); + + // 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) { - LogMsg("ERROR: create_reply - data length less than lenght of required fields"); - return NULL; + fprintf(fp, "%d\n", (int)getpid()); + fclose(fp); } - - 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) + if (skts) { - 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) + for (i = 0; i < count; i++) + if (dnssd_SocketValid(skts[i]) && !uds_socket_setup(skts[i])) + goto error; + } + else + { + listenfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (!dnssd_SocketValid(listenfd)) { - freeL("deliver_async_error", reply); - return -1; + my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed"); + goto error; + } + + mDNSPlatformMemZero(&laddr, sizeof(laddr)); + + #if defined(USE_TCP_LOOPBACK) + { + laddr.sin_family = AF_INET; + laddr.sin_port = htons(MDNS_TCP_SERVERPORT); + laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); + if (ret < 0) + { + my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed"); + goto error; + } + } + #else + { + mode_t mask = umask(0); + unlink(MDNS_UDS_SERVERPATH); // OK if this fails + laddr.sun_family = AF_LOCAL; + #ifndef NOT_HAVE_SA_LEN + // According to Stevens (section 3.2), there is no portable way to + // determine whether sa_len is defined on a particular platform. + laddr.sun_len = sizeof(struct sockaddr_un); + #endif + if (strlen(MDNS_UDS_SERVERPATH) >= sizeof(laddr.sun_path)) + { + LogMsg("ERROR: MDNS_UDS_SERVERPATH must be < %d characters", (int)sizeof(laddr.sun_path)); + goto error; + } + mDNSPlatformStrCopy(laddr.sun_path, MDNS_UDS_SERVERPATH); + ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); + umask(mask); + if (ret < 0) + { + my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed"); + goto error; + } } - 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 + #endif + + if (!uds_socket_setup(listenfd)) goto error; + } + +#if !defined(PLATFORM_NO_RLIMIT) + { + // Set maximum number of open file descriptors + #define MIN_OPENFILES 10240 + struct rlimit maxfds, newfds; + + // Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>) + // you have to get and set rlimits once before getrlimit will return sensible values + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); + + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES; + newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES; + if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur) + if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); + + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max); + debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur); + } +#endif + + // We start a "LocalOnly" query looking for Automatic Browse Domain records. + // When Domain Enumeration in uDNS.c finds an "lb" record from the network, its "FoundDomain" routine + // creates a "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked + mDNS_GetDomains(&mDNSStorage, &mDNSStorage.AutomaticBrowseDomainQ, mDNS_DomainTypeBrowseAutomatic, + mDNSNULL, mDNSInterface_LocalOnly, AutomaticBrowseDomainChange, mDNSNULL); + + // Add "local" as recommended registration domain ("dns-sd -E"), recommended browsing domain ("dns-sd -F"), and automatic browsing domain + RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeRegistration); + RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeBrowse); + AddAutoBrowseDomain(0, &localdomain); + + udsserver_handle_configchange(&mDNSStorage); return 0; + +error: + + my_perror("ERROR: udsserver_init"); + return -1; +} + +mDNSexport int udsserver_exit(void) +{ + // Cancel all outstanding client requests + while (all_requests) AbortUnlinkAndFree(all_requests); + + // Clean up any special mDNSInterface_LocalOnly records we created, both the entries for "local" we + // created in udsserver_init, and others we created as a result of reading local configuration data + while (LocalDomainEnumRecords) + { + ARListElem *rem = LocalDomainEnumRecords; + LocalDomainEnumRecords = LocalDomainEnumRecords->next; + mDNS_Deregister(&mDNSStorage, &rem->ar); } -mDNSlocal void abort_request(request_state *rs) + // If the launching environment created no listening socket, + // that means we created it ourselves, so we should clean it up on exit + if (dnssd_SocketValid(listenfd)) + { + dnssd_close(listenfd); +#if !defined(USE_TCP_LOOPBACK) + // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody" + // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket. + // It would be nice if we could find a solution to this problem + if (unlink(MDNS_UDS_SERVERPATH)) + debugf("Unable to remove %s", MDNS_UDS_SERVERPATH); +#endif + } + + if (PID_FILE[0]) unlink(PID_FILE); + + return 0; +} + +mDNSlocal void LogClientInfo(mDNS *const m, request_state *req) +{ + char prefix[16]; + if (req->primary) + mDNS_snprintf(prefix, sizeof(prefix), " -> "); + else + mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd); + + if (!req->terminate) + LogMsgNoIdent("%s No operation yet on this socket", prefix); + else if (req->terminate == connection_termination) { - reply_state *rep, *ptr; + int num_records = 0, num_ops = 0; + const registered_record_entry *p; + request_state *r; + for (p = req->u.reg_recs; p; p=p->next) num_records++; + for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++; + LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)", + prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "", + req->process_id, req->pid_name); + for (p = req->u.reg_recs; p; p=p->next) + LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s PID[%d](%s)", p->key, ARDisplayString(m, p->rr), + req->process_id, req->pid_name); + for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *ptr; + char anonstr[256]; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + LogMsgNoIdent("%s DNSServiceRegister %##s%s %u/%u PID[%d](%s)", + (ptr == req->u.servicereg.instances) ? prefix : " ", ptr->srs.RR_SRV.resrec.name->c, + AnonDataToString(ptr->srs.AnonData, 0, anonstr, sizeof(anonstr)), mDNSVal16(req->u.servicereg.port), + SRS_PORT(&ptr->srs), req->process_id, req->pid_name); + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + char anonstr[256]; + for (blist = req->u.browser.browsers; blist; blist = blist->next) + LogMsgNoIdent("%s DNSServiceBrowse %##s%s PID[%d](%s)", + (blist == req->u.browser.browsers) ? prefix : " ",blist->q.qname.c, + AnonDataToString(req->u.browser.AnonData, 0, anonstr, sizeof(anonstr)), req->process_id, req->pid_name); + } + else if (req->terminate == resolve_termination_callback) + LogMsgNoIdent("%s DNSServiceResolve %##s PID[%d](%s)", + prefix, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name); + else if (req->terminate == queryrecord_termination_callback) + LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s) PID[%d](%s)", + prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), req->process_id, req->pid_name); + else if (req->terminate == enum_termination_callback) + LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s PID[%d](%s)", prefix, req->u.enumeration.q_all.qname.c, + req->process_id, req->pid_name); + else if (req->terminate == port_mapping_termination_callback) + LogMsgNoIdent("%s DNSServiceNATPortMapping %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)", + prefix, + req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", + req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", + mDNSVal16(req->u.pm.NATinfo.IntPort), + mDNSVal16(req->u.pm.ReqExt), + &req->u.pm.NATinfo.ExternalAddress, + mDNSVal16(req->u.pm.NATinfo.ExternalPort), + req->u.pm.NATinfo.NATLease, + req->u.pm.NATinfo.Lifetime, + req->process_id, req->pid_name); + else if (req->terminate == addrinfo_termination_callback) + LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s PID[%d](%s)", prefix, + req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", + req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", + req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name); + else + LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate); +} - 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 +mDNSlocal void GetMcastClients(request_state *req) +{ + if (req->terminate == connection_termination) + { + int num_records = 0, num_ops = 0; + const registered_record_entry *p; + request_state *r; + for (p = req->u.reg_recs; p; p=p->next) + num_records++; + for (r = req->next; r; r=r->next) + if (r->primary == req) + num_ops++; + for (p = req->u.reg_recs; p; p=p->next) + { + if (!AuthRecord_uDNS(p->rr)) + n_mrecords++; + } + for (r = req->next; r; r=r->next) + if (r->primary == req) + GetMcastClients(r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *ptr; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + { + if (!AuthRecord_uDNS(&ptr->srs.RR_SRV)) + n_mrecords++; + } + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + for (blist = req->u.browser.browsers; blist; blist = blist->next) + { + if (mDNSOpaque16IsZero(blist->q.TargetQID)) + n_mquests++; + } + } + else if (req->terminate == resolve_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.resolve.qsrv.TargetQID)) && (req->u.resolve.qsrv.ThisQInterval > 0)) + n_mquests++; + } + else if (req->terminate == queryrecord_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0)) + n_mquests++; + } + else if (req->terminate == addrinfo_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0)) + n_mquests++; + } + else + { + return; + } +} - // 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); - } +mDNSlocal void LogMcastClientInfo(request_state *req) +{ + if (!req->terminate) + LogMcastNoIdent("No operation yet on this socket"); + else if (req->terminate == connection_termination) + { + int num_records = 0, num_ops = 0; + const registered_record_entry *p; + request_state *r; + for (p = req->u.reg_recs; p; p=p->next) + num_records++; + for (r = req->next; r; r=r->next) + if (r->primary == req) + num_ops++; + for (p = req->u.reg_recs; p; p=p->next) + { + if (!AuthRecord_uDNS(p->rr)) + LogMcastNoIdent("R: -> DNSServiceRegisterRecord: %##s %s PID[%d](%s)", p->rr->resrec.name->c, + DNSTypeName(p->rr->resrec.rrtype), req->process_id, req->pid_name, i_mcount++); + } + for (r = req->next; r; r=r->next) + if (r->primary == req) + LogMcastClientInfo(r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *ptr; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + { + if (!AuthRecord_uDNS(&ptr->srs.RR_SRV)) + LogMcastNoIdent("R: DNSServiceRegister: %##s %u/%u PID[%d](%s)", ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), + SRS_PORT(&ptr->srs), req->process_id, req->pid_name, i_mcount++); + } + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + for (blist = req->u.browser.browsers; blist; blist = blist->next) + { + if (mDNSOpaque16IsZero(blist->q.TargetQID)) + LogMcastNoIdent("Q: DNSServiceBrowse %##s %s PID[%d](%s)", blist->q.qname.c, DNSTypeName(blist->q.qtype), + req->process_id, req->pid_name, i_mcount++); + } + } + else if (req->terminate == resolve_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.resolve.qsrv.TargetQID)) && (req->u.resolve.qsrv.ThisQInterval > 0)) + LogMcastNoIdent("Q: DNSServiceResolve %##s %s PID[%d](%s)", req->u.resolve.qsrv.qname.c, DNSTypeName(req->u.resolve.qsrv.qtype), + req->process_id, req->pid_name, i_mcount++); + } + else if (req->terminate == queryrecord_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0)) + LogMcastNoIdent("Q: DNSServiceQueryRecord %##s %s PID[%d](%s)", req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), + req->process_id, req->pid_name, i_mcount++); + } + else if (req->terminate == addrinfo_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0)) + LogMcastNoIdent("Q: DNSServiceGetAddrInfo %s%s %##s PID[%d](%s)", + req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", + req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", + req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name, i_mcount++); + } + else + { + return; + } - if (rs->u_err) +} + +mDNSlocal char *RecordTypeName(mDNSu8 rtype) +{ + switch (rtype) + { + case kDNSRecordTypeUnregistered: return ("Unregistered "); + case kDNSRecordTypeDeregistering: return ("Deregistering"); + case kDNSRecordTypeUnique: return ("Unique "); + case kDNSRecordTypeAdvisory: return ("Advisory "); + case kDNSRecordTypeShared: return ("Shared "); + case kDNSRecordTypeVerified: return ("Verified "); + case kDNSRecordTypeKnownUnique: return ("KnownUnique "); + default: return("Unknown"); + } +} + +mDNSlocal void LogEtcHosts(mDNS *const m) +{ + mDNSBool showheader = mDNStrue; + const AuthRecord *ar; + mDNSu32 slot; + AuthGroup *ag; + int count = 0; + int authslot = 0; + mDNSBool truncated = 0; + + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + { + if (m->rrauth.rrauth_hash[slot]) authslot++; + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + for (ar = ag->members; ar; ar = ar->next) + { + if (ar->RecordCallback != FreeEtcHosts) continue; + if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); } + + // Print a maximum of 50 records + if (count++ >= 50) { truncated = mDNStrue; continue; } + if (ar->ARType == AuthRecordLocalOnly) + { + if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly) + LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + else + { + mDNSu32 scopeid = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID; + LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar)); + } + } + } + } + + if (showheader) LogMsgNoIdent("<None>"); + else if (truncated) LogMsgNoIdent("<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot); +} + +mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m) +{ + mDNSBool showheader = mDNStrue; + const AuthRecord *ar; + mDNSu32 slot; + AuthGroup *ag; + + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + { + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + for (ar = ag->members; ar; ar = ar->next) + { + if (ar->RecordCallback == FreeEtcHosts) continue; + if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); } + + // Print a maximum of 400 records + if (ar->ARType == AuthRecordLocalOnly) + LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + else if (ar->ARType == AuthRecordP2P) + LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + } + } + + if (showheader) LogMsgNoIdent("<None>"); +} + +mDNSlocal char *AnonInfoToString(AnonymousInfo *ai, char *anonstr, int anstrlen) +{ + anonstr[0] = 0; + if (ai && ai->AnonData) + { + return (AnonDataToString(ai->AnonData, ai->AnonDataLen, anonstr, anstrlen)); + } + return anonstr; +} + +mDNSlocal void LogOneAuthRecord(mDNS *const m, const AuthRecord *ar, mDNSs32 now, const char *const ifname) +{ + char anstr[256]; + if (AuthRecord_uDNS(ar)) + { + LogMsgNoIdent("%7d %7d %7d %7d %s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, + ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, + ar->state, ARDisplayString(m, ar)); + } + else + { + LogMsgNoIdent("%7d %7d %7d %7s %s%s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, + ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, + ifname ? ifname : "ALL", + ARDisplayString(m, ar), AnonInfoToString(ar->resrec.AnonInfo, anstr, sizeof(anstr))); + } +} + +mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy) +{ + mDNSBool showheader = mDNStrue; + const AuthRecord *ar; + OwnerOptData owner = zeroOwner; + for (ar = ResourceRecords; ar; ar=ar->next) + { + const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID); + if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL)) { - freeL("abort_request", rs->u_err); - rs->u_err = NULL; + if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire State"); } + if (proxy) (*proxy)++; + if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner))) + { + owner = ar->WakeUp; + if (owner.password.l[0]) + LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); + else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC)) + LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq); + else + LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq); + } + if (AuthRecord_uDNS(ar)) + { + LogOneAuthRecord(m, ar, now, ifname); + } + else if (ar->ARType == AuthRecordLocalOnly) + { + LogMsgNoIdent(" LO %s", ARDisplayString(m, ar)); + } + else if (ar->ARType == AuthRecordP2P) + { + LogMsgNoIdent(" PP %s", ARDisplayString(m, ar)); + } + else + { + LogOneAuthRecord(m, ar, now, ifname); + if (ar->resrec.AnonInfo) + { + ResourceRecord *nsec3 = ar->resrec.AnonInfo->nsec3RR; + // We just print the values from the AuthRecord to keep it nicely aligned though + // all we want here is the nsec3 information. + LogMsgNoIdent("%7d %7d %7d %7s %s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, + ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, + ifname ? ifname : "ALL", + RRDisplayString(m, nsec3)); + } + } } } + if (showheader) LogMsgNoIdent("<None>"); +} + +mDNSlocal void PrintOneCacheRecord(mDNS *const m, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed) +{ + LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s", + slot, + cr->CRActiveQuestion ? "*" : " ", + remain, + ifname ? ifname : "-U-", + (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : + (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", + DNSTypeName(cr->resrec.rrtype), + CRDisplayString(m, cr)); + (*CacheUsed)++; +} + +mDNSlocal void PrintCachedRecords(mDNS *const m, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed) +{ + CacheRecord *nsec; + CacheRecord *soa; + nsec = cr->nsec; + + // The records that are cached under the main cache record like nsec, soa don't have + // their own lifetime. If the main cache record expires, they also expire. + while (nsec) + { + PrintOneCacheRecord(m, nsec, slot, remain, ifname, CacheUsed); + nsec = nsec->next; + } + soa = cr->soa; + if (soa) + { + PrintOneCacheRecord(m, soa, slot, remain, ifname, CacheUsed); + } + if (cr->resrec.AnonInfo) + { + ResourceRecord *nsec3 = cr->resrec.AnonInfo->nsec3RR; + // Even though it is a resource record, we print the sameway + // as a cache record so that it aligns properly. + if (nsec3) + { + LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s", + slot, + " ", + remain, + ifname ? ifname : "-U-", + (nsec3->RecordType == kDNSRecordTypePacketNegative) ? "-" : + (nsec3->RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", + DNSTypeName(nsec3->rrtype), + RRDisplayString(m, nsec3)); + } + } +} -mDNSlocal void unlink_request(request_state *rs) +mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen) +{ + adstr[0] = 0; + if (ad) { - request_state *ptr; + int len; + char *orig = adstr; + + // If the caller is lazy to compute the length, we do it for them. + if (!adlen) + len = strlen((const char *)ad); + else + len = adlen; + + // Print the anondata within brackets. Hence, we need space for two + // brackets and a NULL byte. + if (len > (adstrlen - 3)) + len = adstrlen - 3; + + *adstr++ = '('; + mDNSPlatformMemCopy(adstr, ad, len); + adstr[len] = ')'; + adstr[len+1] = 0; + return orig; + } + return adstr; +} + +mDNSexport void LogMDNSStatistics(mDNS *const m) +{ + LogMsgNoIdent("--- MDNS Statistics ---"); - if (rs == all_requests) + LogMsgNoIdent("Name Conflicts %u", m->mDNSStats.NameConflicts); + LogMsgNoIdent("KnownUnique Name Conflicts %u", m->mDNSStats.KnownUniqueNameConflicts); + LogMsgNoIdent("Duplicate Query Suppressions %u", m->mDNSStats.DupQuerySuppressions); + LogMsgNoIdent("KA Suppressions %u", m->mDNSStats.KnownAnswerSuppressions); + LogMsgNoIdent("KA Multiple Packets %u", m->mDNSStats.KnownAnswerMultiplePkts); + LogMsgNoIdent("Poof Cache Deletions %u", m->mDNSStats.PoofCacheDeletions); + LogMsgNoIdent("--------------------------------"); + + LogMsgNoIdent("Multicast packets Sent %u", m->MulticastPacketsSent); + LogMsgNoIdent("Multicast packets Received %u", m->MPktNum); + LogMsgNoIdent("Remote Subnet packets %u", m->RemoteSubnet); + LogMsgNoIdent("QU questions received %u", m->mDNSStats.UnicastBitInQueries); + LogMsgNoIdent("Normal multicast questions %u", m->mDNSStats.NormalQueries); + LogMsgNoIdent("Answers for questions %u", m->mDNSStats.MatchingAnswersForQueries); + LogMsgNoIdent("Unicast responses %u", m->mDNSStats.UnicastResponses); + LogMsgNoIdent("Multicast responses %u", m->mDNSStats.MulticastResponses); + LogMsgNoIdent("Unicast response Demotions %u", m->mDNSStats.UnicastDemotedToMulticast); + LogMsgNoIdent("--------------------------------"); + + LogMsgNoIdent("Sleeps %u", m->mDNSStats.Sleeps); + LogMsgNoIdent("Wakeups %u", m->mDNSStats.Wakes); + LogMsgNoIdent("Interface UP events %u", m->mDNSStats.InterfaceUp); + LogMsgNoIdent("Interface UP Flap events %u", m->mDNSStats.InterfaceUpFlap); + LogMsgNoIdent("Interface Down events %u", m->mDNSStats.InterfaceDown); + LogMsgNoIdent("Interface DownFlap events %u", m->mDNSStats.InterfaceDownFlap); + LogMsgNoIdent("Cache refresh queries %u", m->mDNSStats.CacheRefreshQueries); + LogMsgNoIdent("Cache refreshed %u", m->mDNSStats.CacheRefreshed); + LogMsgNoIdent("Wakeup on Resolves %u", m->mDNSStats.WakeOnResolves); +} + +mDNSexport void udsserver_info(mDNS *const m) +{ + const mDNSs32 now = mDNS_TimeNow(m); + mDNSu32 CacheUsed = 0, CacheActive = 0, slot; + int ProxyA = 0, ProxyD = 0; + const CacheGroup *cg; + const CacheRecord *cr; + const DNSQuestion *q; + const DNameListElem *d; + const SearchListElem *s; + + LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); + + LogMsgNoIdent("------------ Cache -------------"); + LogMsgNoIdent("Slt Q TTL if U Type rdlen"); + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) { - all_requests = all_requests->next; - freeL("unlink_request", rs); - return; + CacheUsed++; // Count one cache entity for the CacheGroup object + for (cr = cg->members; cr; cr=cr->next) + { + const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; + const char *ifname; + mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID; + if (!InterfaceID && cr->resrec.rDNSServer && cr->resrec.rDNSServer->scoped) + InterfaceID = cr->resrec.rDNSServer->interface; + ifname = InterfaceNameForID(m, InterfaceID); + if (cr->CRActiveQuestion) CacheActive++; + PrintOneCacheRecord(m, cr, slot, remain, ifname, &CacheUsed); + PrintCachedRecords(m, cr, slot, remain, ifname, &CacheUsed); + } + } + } + + if (m->rrcache_totalused != CacheUsed) + LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); + if (m->rrcache_active != CacheActive) + LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive); + LogMsgNoIdent("Cache currently contains %lu entities; %lu referenced by active questions", CacheUsed, CacheActive); + + LogMsgNoIdent("--------- Auth Records ---------"); + LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL); + + LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------"); + LogLocalOnlyAuthRecords(m); + + LogMsgNoIdent("--------- /etc/hosts ---------"); + LogEtcHosts(m); + + LogMsgNoIdent("------ Duplicate Records -------"); + LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL); + + LogMsgNoIdent("----- Auth Records Proxied -----"); + LogAuthRecords(m, now, m->ResourceRecords, &ProxyA); + + LogMsgNoIdent("-- Duplicate Records Proxied ---"); + LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD); + + LogMsgNoIdent("---------- Questions -----------"); + if (!m->Questions) LogMsgNoIdent("<None>"); + else + { + char anonstr[256]; + CacheUsed = 0; + CacheActive = 0; + LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name"); + for (q = m->Questions; q; q=q->next) + { + mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond; + mDNSs32 n = (NextQSendTime(q) - now) / mDNSPlatformOneSecond; + char *ifname = InterfaceNameForID(m, q->InterfaceID); + CacheUsed++; + if (q->ThisQInterval) CacheActive++; + LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s%s", + i, n, + ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", + mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"), + PrivateQuery(q) ? "P" : q->ValidationRequired ? "V" : q->ValidatingResponse ? "R" : " ", + q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf, + q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, + AnonInfoToString(q->AnonInfo, anonstr, sizeof(anonstr)), + q->DuplicateOf ? " (dup)" : ""); } - for(ptr = all_requests; ptr->next; ptr = ptr->next) - if (ptr->next == rs) + LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive); + } + + LogMsgNoIdent("----- Local-Only Questions -----"); + if (!m->LocalOnlyQuestions) LogMsgNoIdent("<None>"); + else for (q = m->LocalOnlyQuestions; q; q=q->next) + LogMsgNoIdent(" %5d %-6s%##s%s", + q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); + + LogMsgNoIdent("---- Active UDS Client Requests ----"); + if (!all_requests) LogMsgNoIdent("<None>"); + else + { + request_state *req, *r; + for (req = all_requests; req; req=req->next) + { + if (req->primary) // If this is a subbordinate operation, check that the parent is in the list { - ptr->next = rs->next; - freeL("unlink_request", rs); - return; + for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent; + LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd); + } + // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info + LogClientInfo(m, req); +foundparent:; } } -//hack to search-replace perror's to LogMsg's -mDNSlocal void my_perror(char *errmsg) + LogMsgNoIdent("-------- NAT Traversals --------"); + LogMsgNoIdent("ExtAddress %.4a Retry %d Interval %d", + &m->ExtAddress, + m->retryGetAddr ? (m->retryGetAddr - now) / mDNSPlatformOneSecond : 0, + m->retryIntervalGetAddr / mDNSPlatformOneSecond); + if (m->NATTraversals) { - LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno())); + const NATTraversalInfo *nat; + for (nat = m->NATTraversals; nat; nat=nat->next) + { + LogMsgNoIdent("%p %s Int %5d %s Err %d Retry %5d Interval %5d Expire %5d Req %.4a:%d Ext %.4a:%d", + nat, + nat->Protocol ? (nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP") : "ADD", + mDNSVal16(nat->IntPort), + (nat->lastSuccessfulProtocol == NATTProtocolNone ? "None " : + nat->lastSuccessfulProtocol == NATTProtocolNATPMP ? "NAT-PMP " : + nat->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" : + nat->lastSuccessfulProtocol == NATTProtocolPCP ? "PCP " : + /* else */ "Unknown " ), + nat->Result, + nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, + nat->retryInterval / mDNSPlatformOneSecond, + nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0, + &nat->NewAddress, mDNSVal16(nat->RequestedPort), + &nat->ExternalAddress, mDNSVal16(nat->ExternalPort)); + } } -// 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. + LogMsgNoIdent("--------- AuthInfoList ---------"); + if (!m->AuthInfoList) LogMsgNoIdent("<None>"); + else + { + const DomainAuthInfo *a; + for (a = m->AuthInfoList; a; a = a->next) + { + LogMsgNoIdent("%##s %##s %##s %d %d %.16a%s", + a->domain.c, a->keyname.c, + a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), + (a->deltime ? (a->deltime - now) : 0), + &a->AutoTunnelInnerAddress, a->AutoTunnel ? " AutoTunnel" : ""); + } + } -mDNSlocal int validate_message(request_state *rstate) + #if APPLE_OSX_mDNSResponder + LogMsgNoIdent("--------- TunnelClients --------"); + if (!m->TunnelClients) LogMsgNoIdent("<None>"); + else { - 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); - + const ClientTunnel *c; + for (c = m->TunnelClients; c; c = c->next) + LogMsgNoIdent("%##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d", + c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval); } + #endif // APPLE_OSX_mDNSResponder -mDNSlocal uint32_t dnssd_htonl(uint32_t l) - { - uint32_t ret; - char * data; + LogMsgNoIdent("---------- Misc State ----------"); - data = (char*) &ret; + LogMsgNoIdent("PrimaryMAC: %.6a", &m->PrimaryMAC); - put_long(l, &data); + LogMsgNoIdent("m->SleepState %d (%s) seq %d", + m->SleepState, + m->SleepState == SleepState_Awake ? "Awake" : + m->SleepState == SleepState_Transferring ? "Transferring" : + m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", + m->SleepSeqNum); - return ret; - } + if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service"); +#ifndef SPC_DISABLED + else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c); +#endif + if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD); + else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d ≠ %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords); -#if defined(_WIN32) + LogMsgNoIdent("------ Auto Browse Domains -----"); + if (!AutoBrowseDomains) LogMsgNoIdent("<None>"); + else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); -mDNSlocal char * win32_strerror(int inErrorCode) - { - static char buffer[1024]; - DWORD n; + LogMsgNoIdent("--- Auto Registration Domains --"); + if (!AutoRegistrationDomains) LogMsgNoIdent("<None>"); + else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); - memset(buffer, 0, sizeof(buffer)); + LogMsgNoIdent("--- Search Domains --"); + if (!SearchList) LogMsgNoIdent("<None>"); + else + { + for (s=SearchList; s; s=s->next) + { + char *ifname = InterfaceNameForID(m, s->InterfaceID); + LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : ""); + } + } + LogInfo("--- Trust Anchors ---"); + if (!m->TrustAnchors) + { + LogInfo("<None>"); + } + else + { + TrustAnchor *ta; + mDNSu8 fromTimeBuf[64]; + mDNSu8 untilTimeBuf[64]; - n = FormatMessageA( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - (DWORD) inErrorCode, - MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), - buffer, - sizeof( buffer ), - NULL ); + for (ta=m->TrustAnchors; ta; ta=ta->next) + { + mDNSPlatformFormatTime((unsigned long)ta->validFrom, fromTimeBuf, sizeof(fromTimeBuf)); + mDNSPlatformFormatTime((unsigned long)ta->validUntil, untilTimeBuf, sizeof(untilTimeBuf)); + LogInfo("%##s %d %d %d %d %s %s", ta->zone.c, ta->rds.keyTag, + ta->rds.alg, ta->rds.digestType, ta->digestLen, fromTimeBuf, untilTimeBuf); + } + } - if( n > 0 ) - { - // Remove any trailing CR's or LF's since some messages have them. + LogInfo("--- DNSSEC Statistics ---"); + + LogInfo("Next Stats Time %u", m->NextStatLogTime - mDNSPlatformUTC()); + LogMsgNoIdent("Unicast Cache size %u", m->rrcache_totalused_unicast); + LogInfo("DNSSEC Cache size %u", m->DNSSECStats.TotalMemUsed); + if (m->rrcache_totalused_unicast) + LogInfo("DNSSEC usage percentage %u", ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast); + LogInfo("DNSSEC Extra Packets (0 to 2) %u", m->DNSSECStats.ExtraPackets0); + LogInfo("DNSSEC Extra Packets (3 to 6) %u", m->DNSSECStats.ExtraPackets3); + LogInfo("DNSSEC Extra Packets (7 to 9) %u", m->DNSSECStats.ExtraPackets7); + LogInfo("DNSSEC Extra Packets ( >= 10) %u", m->DNSSECStats.ExtraPackets10); + + LogInfo("DNSSEC Latency (0 to 4ms) %u", m->DNSSECStats.Latency0); + LogInfo("DNSSEC Latency (4 to 9ms) %u", m->DNSSECStats.Latency5); + LogInfo("DNSSEC Latency (10 to 19ms) %u", m->DNSSECStats.Latency10); + LogInfo("DNSSEC Latency (20 to 49ms) %u", m->DNSSECStats.Latency20); + LogInfo("DNSSEC Latency (50 to 99ms) %u", m->DNSSECStats.Latency50); + LogInfo("DNSSEC Latency ( >=100ms) %u", m->DNSSECStats.Latency100); + + LogInfo("DNSSEC Secure Status %u", m->DNSSECStats.SecureStatus); + LogInfo("DNSSEC Insecure Status %u", m->DNSSECStats.InsecureStatus); + LogInfo("DNSSEC Indeterminate Status %u", m->DNSSECStats.IndeterminateStatus); + LogInfo("DNSSEC Bogus Status %u", m->DNSSECStats.BogusStatus); + LogInfo("DNSSEC NoResponse Status %u", m->DNSSECStats.NoResponseStatus); + LogInfo("DNSSEC Probes sent %u", m->DNSSECStats.NumProbesSent); + LogInfo("DNSSEC Msg Size (<=1024) %u", m->DNSSECStats.MsgSize0); + LogInfo("DNSSEC Msg Size (<=2048) %u", m->DNSSECStats.MsgSize1); + LogInfo("DNSSEC Msg Size (> 2048) %u", m->DNSSECStats.MsgSize2); + + LogMDNSStatistics(m); + + LogMsgNoIdent("---- Task Scheduling Timers ----"); + + if (!m->NewQuestions) + LogMsgNoIdent("NewQuestion <NONE>"); + else + LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)", + m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now, + m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); - while( ( n > 0 ) && isspace( ( (unsigned char *) buffer)[ n - 1 ] ) ) - { - buffer[ --n ] = '\0'; - } - } + if (!m->NewLocalOnlyQuestions) + LogMsgNoIdent("NewLocalOnlyQuestions <NONE>"); + else + LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)", + m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); - return buffer; - } + if (!m->NewLocalRecords) + LogMsgNoIdent("NewLocalRecords <NONE>"); + else + LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords)); + + LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>"); + LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " <NONE>"); + LogMsgNoIdent("m->AutoTunnelRelayAddr %.16a", &m->AutoTunnelRelayAddr); + LogMsgNoIdent("m->WABBrowseQueriesCount %d", m->WABBrowseQueriesCount); + LogMsgNoIdent("m->WABLBrowseQueriesCount %d", m->WABLBrowseQueriesCount); + LogMsgNoIdent("m->WABRegQueriesCount %d", m->WABRegQueriesCount); + LogMsgNoIdent("m->mDNSOppCaching %d", m->mDNSOppCaching); + LogMsgNoIdent("m->AutoTargetServices %d", m->AutoTargetServices); + +#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now) + + LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)"); + LogMsgNoIdent("m->timenow %08X %11d", now, now); + LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust); + LogTimer("m->NextScheduledEvent ", m->NextScheduledEvent); + +#ifndef UNICAST_DISABLED + LogTimer("m->NextuDNSEvent ", m->NextuDNSEvent); + LogTimer("m->NextSRVUpdate ", m->NextSRVUpdate); + LogTimer("m->NextScheduledNATOp ", m->NextScheduledNATOp); + LogTimer("m->retryGetAddr ", m->retryGetAddr); +#endif + LogTimer("m->NextCacheCheck ", m->NextCacheCheck); + LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS); + LogTimer("m->NextScheduledKA ", m->NextScheduledKA); + LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry); + LogTimer("m->DelaySleep ", m->DelaySleep); + + LogTimer("m->NextScheduledQuery ", m->NextScheduledQuery); + LogTimer("m->NextScheduledProbe ", m->NextScheduledProbe); + LogTimer("m->NextScheduledResponse", m->NextScheduledResponse); + + LogTimer("m->SuppressSending ", m->SuppressSending); + LogTimer("m->SuppressProbes ", m->SuppressProbes); + LogTimer("m->ProbeFailTime ", m->ProbeFailTime); + LogTimer("m->DelaySleep ", m->DelaySleep); + LogTimer("m->SleepLimit ", m->SleepLimit); + LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime); +} + +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING +mDNSexport void uds_validatelists(void) +{ + const request_state *req, *p; + for (req = all_requests; req; req=req->next) + { + if (req->next == (request_state *)~0 || (req->sd < 0 && req->sd != -2)) + LogMemCorruption("UDS request list: %p is garbage (%d)", req, req->sd); + + if (req->primary == req) + LogMemCorruption("UDS request list: req->primary should not point to self %p/%d", req, req->sd); + + if (req->primary && req->replies) + LogMemCorruption("UDS request list: Subordinate request %p/%d/%p should not have replies (%p)", + req, req->sd, req->primary && req->replies); + + p = req->primary; + if ((long)p & 3) + LogMemCorruption("UDS request list: req %p primary %p is misaligned (%d)", req, p, req->sd); + else if (p && (p->next == (request_state *)~0 || (p->sd < 0 && p->sd != -2))) + LogMemCorruption("UDS request list: req %p primary %p is garbage (%d)", req, p, p->sd); + + reply_state *rep; + for (rep = req->replies; rep; rep=rep->next) + if (rep->next == (reply_state *)~0) + LogMemCorruption("UDS req->replies: %p is garbage", rep); + + if (req->terminate == connection_termination) + { + registered_record_entry *r; + for (r = req->u.reg_recs; r; r=r->next) + if (r->next == (registered_record_entry *)~0) + LogMemCorruption("UDS req->u.reg_recs: %p is garbage", r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *s; + for (s = req->u.servicereg.instances; s; s=s->next) + if (s->next == (service_instance *)~0) + LogMemCorruption("UDS req->u.servicereg.instances: %p is garbage", s); + } + else if (req->terminate == browse_termination_callback) + { + browser_t *b; + for (b = req->u.browser.browsers; b; b=b->next) + if (b->next == (browser_t *)~0) + LogMemCorruption("UDS req->u.browser.browsers: %p is garbage", b); + } + } + + DNameListElem *d; + for (d = SCPrefBrowseDomains; d; d=d->next) + if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) + LogMemCorruption("SCPrefBrowseDomains: %p is garbage (%d)", d, d->name.c[0]); + + ARListElem *b; + for (b = LocalDomainEnumRecords; b; b=b->next) + if (b->next == (ARListElem *)~0 || b->ar.resrec.name->c[0] > 63) + LogMemCorruption("LocalDomainEnumRecords: %p is garbage (%d)", b, b->ar.resrec.name->c[0]); + + for (d = AutoBrowseDomains; d; d=d->next) + if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) + LogMemCorruption("AutoBrowseDomains: %p is garbage (%d)", d, d->name.c[0]); + + for (d = AutoRegistrationDomains; d; d=d->next) + if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63) + LogMemCorruption("AutoRegistrationDomains: %p is garbage (%d)", d, d->name.c[0]); +} +#endif // APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING + +mDNSlocal int send_msg(request_state *const req) +{ + reply_state *const rep = req->replies; // Send the first waiting reply + ssize_t nwriten; + if (req->no_reply) return(t_complete); + + ConvertHeaderBytes(rep->mhdr); + nwriten = send(req->sd, (char *)&rep->mhdr + rep->nwriten, rep->totallen - rep->nwriten, 0); + ConvertHeaderBytes(rep->mhdr); + + if (nwriten < 0) + { + if (dnssd_errno == dnssd_EINTR || dnssd_errno == dnssd_EWOULDBLOCK) nwriten = 0; + else + { +#if !defined(PLATFORM_NO_EPIPE) + if (dnssd_errno == EPIPE) + return(req->ts = t_terminated); + else #endif + { + LogMsg("send_msg ERROR: failed to write %d of %d bytes to fd %d errno %d (%s)", + rep->totallen - rep->nwriten, rep->totallen, req->sd, dnssd_errno, dnssd_strerror(dnssd_errno)); + return(t_error); + } + } + } + rep->nwriten += nwriten; + return (rep->nwriten == rep->totallen) ? t_complete : t_morecoming; +} + +mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) +{ + mDNSs32 now = mDNS_TimeNow(&mDNSStorage); + request_state **req = &all_requests; + + while (*req) + { + request_state *const r = *req; + + if (r->terminate == resolve_termination_callback) + if (r->u.resolve.ReportTime && now - r->u.resolve.ReportTime >= 0) + { + r->u.resolve.ReportTime = 0; + LogMsgNoIdent("Client application bug PID[%d](%s) : DNSServiceResolve(%##s) active for over two minutes. " + "This places considerable burden on the network.", r->process_id, r->pid_name, r->u.resolve.qsrv.qname.c); + } + + // Note: Only primary req's have reply lists, not subordinate req's. + while (r->replies) // Send queued replies + { + transfer_state result; + if (r->replies->next) + r->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing); + result = send_msg(r); // Returns t_morecoming if buffer full because client is not reading + if (result == t_complete) + { + reply_state *fptr = r->replies; + r->replies = r->replies->next; + freeL("reply_state/udsserver_idle", fptr); + r->time_blocked = 0; // reset failure counter after successful send + r->unresponsiveness_reports = 0; + continue; + } + else if (result == t_terminated || result == t_error) + { + LogMsg("%3d: Could not write data to clientPID[%d](%s) because of error - aborting connection", r->sd, r->process_id, r->pid_name); + LogClientInfo(&mDNSStorage, r); + abort_request(r); + } + break; + } + + if (r->replies) // If we failed to send everything, check our time_blocked timer + { + if (nextevent - now > mDNSPlatformOneSecond) + nextevent = now + mDNSPlatformOneSecond; + + if (mDNSStorage.SleepState != SleepState_Awake) + r->time_blocked = 0; + else if (!r->time_blocked) + r->time_blocked = NonZeroTime(now); + else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1)) + { + int num = 0; + struct reply_state *x = r->replies; + while (x) + { + num++; + x=x->next; + } + LogMsg("%3d: Could not write data to client PID[%d](%s) after %ld seconds, %d repl%s waiting", + r->sd, r->process_id, r->pid_name, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies"); + if (++r->unresponsiveness_reports >= 60) + { + LogMsg("%3d: Client PID[%d](%s) unresponsive; aborting connection", r->sd, r->process_id, r->pid_name); + LogClientInfo(&mDNSStorage, r); + abort_request(r); + } + } + } + + if (!dnssd_SocketValid(r->sd)) // If this request is finished, unlink it from the list and free the memory + { + // Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree() + *req = r->next; + freeL("request_state/udsserver_idle", r); + } + else + req = &r->next; + } + return nextevent; +} + +struct CompileTimeAssertionChecks_uds_daemon +{ + // Check our structures are reasonable sizes. Including overly-large buffers, or embedding + // other overly-large structures instead of having a pointer to them, can inadvertently + // cause structure sizes (and therefore memory usage) to balloon unreasonably. + char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 1 : -1]; + char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1]; + char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1]; + char sizecheck_browser_t [(sizeof(browser_t) <= 1096) ? 1 : -1]; + char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1]; + char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1]; +}; diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h index 9dec0288b7..ca361172d1 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h @@ -5,114 +5,86 @@ * 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() + File: uds_daemon.h -Revision 1.5 2004/06/18 04:44:58 rpantos -Use platform layer for socket types + Contains: Interfaces necessary to talk to uds_daemon.c. -Revision 1.4 2004/06/12 00:51:58 cheshire -Changes for Windows compatibility + Version: 1.0 -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 int udsserver_init(dnssd_sock_t skts[], mDNSu32 count); extern mDNSs32 udsserver_idle(mDNSs32 nextevent); - -extern void udsserver_info(mDNS *const m); // print out info about current state - -extern void udsserver_handle_configchange(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); +extern void udsserver_info(mDNS *const m); // print out info about current state +extern void udsserver_handle_configchange(mDNS *const m); +extern int udsserver_exit(void); // should be called prior to app exit +extern void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog); +#define LogMcastQ (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastQuestion +#define LogMcastS (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastService +#define LogMcast (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsg +#define LogMcastNoIdent (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsgNoIdent /* 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 +typedef void (*udsEventCallback)(int fd, short filter, void *context); +extern mStatus udsSupportAddFDToEventLoop(dnssd_sock_t fd, udsEventCallback callback, void *context, void **platform_data); +extern int udsSupportReadFD(dnssd_sock_t fd, char* buf, int len, int flags, void *platform_data); +extern mStatus udsSupportRemoveFDFromEventLoop(dnssd_sock_t fd, void *platform_data); // Note: This also CLOSES the file descriptor as well -// 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 DNameListElem *AutoRegistrationDomains; +extern DNameListElem *AutoBrowseDomains; + +extern mDNSs32 ChopSubTypes(char *regtype, char **AnonData); +extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData); extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port); extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result); extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs); + +#if APPLE_OSX_mDNSResponder + +extern void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add); +extern void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add); +// D2D interface support +extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); +extern void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); +extern void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); +extern void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); +extern void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags); +extern void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags); +extern void external_connection_release(const domainname *instance); + +#else // APPLE_OSX_mDNSResponder + +#define external_start_browsing_for_service(A,B,C,D) (void)(A) +#define external_stop_browsing_for_service(A,B,C,D) (void)(A) +#define external_start_advertising_service(A,B) (void)(A) +#define external_stop_advertising_service(A,B) (void)(A) +#define external_start_resolving_service(A,B,C) (void)(A) +#define external_stop_resolving_service(A,B,C) (void)(A) +#define external_connection_release(A) (void)(A) + +#endif // APPLE_OSX_mDNSResponder + +extern const char mDNSResponderVersionString_SCCS[]; +#define mDNSResponderVersionString (mDNSResponderVersionString_SCCS+5) diff --git a/usr/src/cmd/ptools/ptime/ptime.c b/usr/src/cmd/ptools/ptime/ptime.c index 7c9be226e1..fed1ec2ce1 100644 --- a/usr/src/cmd/ptools/ptime/ptime.c +++ b/usr/src/cmd/ptools/ptime/ptime.c @@ -25,7 +25,7 @@ * Portions Copyright 2008 Chad Mynhier */ /* - * Copyright (c) 2014, Joyent, Inc. All rights reserved. + * Copyright 2016 Joyent, Inc. */ #include <stdio.h> @@ -61,6 +61,7 @@ static int Fflag; static int mflag; static int errflg; static int pflag; +static int pfirst; static int ptime_pid(const char *pidstr) @@ -130,22 +131,22 @@ main(int argc, char **argv) } if (pflag) { + char *pp; + exit = 0; (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); - pp = pidarg; - if ((np = strchr(pp, ' ')) != NULL || - (np = strchr(pp, ',')) != NULL) - pflag++; - while (np != NULL) { - *np = '\0'; + + pp = strtok(pidarg, ", "); + if (pp == NULL) { + (void) fprintf(stderr, "%s: invalid argument for -p\n", + command); + return (1); + } + exit = ptime_pid(pp); + while ((pp = strtok(NULL, ", ")) != NULL) { exit |= ptime_pid(pp); - pp = np + 1; - np = strchr(pp, ' '); - if (np == NULL) - np = strchr(pp, ','); } - exit |= ptime_pid(pp); return (exit); } @@ -200,6 +201,8 @@ look(pid_t pid) hrtime_t hrtime; prusage_t *pup = &prusage; + pfirst++; + if (proc_get_psinfo(pid, &psinfo) < 0) return (perr("read psinfo")); @@ -223,8 +226,9 @@ look(pid_t pid) if (!mflag) tsadd(&sys, &sys, &pup->pr_ttime); - (void) fprintf(stderr, "\n"); - if (pflag > 1) + if (!pflag || pfirst > 1) + (void) fprintf(stderr, "\n"); + if (pflag) (void) fprintf(stderr, "%d:\t%.70s\n", (int)psinfo.pr_pid, psinfo.pr_psargs); prtime("real", &real); diff --git a/usr/src/lib/libdns_sd/Makefile.com b/usr/src/lib/libdns_sd/Makefile.com index 4dfbabb468..ca83ad0396 100644 --- a/usr/src/lib/libdns_sd/Makefile.com +++ b/usr/src/lib/libdns_sd/Makefile.com @@ -20,6 +20,7 @@ # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2016 Toomas Soome <tsoome@me.com> # LIBRARY = libdns_sd.a @@ -33,14 +34,10 @@ $(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) SRCDIR = ../common -LDLIBS += -lsocket -lc +LDLIBS += -lsocket -lnsl -lc C99MODE = $(C99_ENABLE) -CPPFLAGS += -I$(SRCDIR) -DNOT_HAVE_SA_LEN - -CERRWARN += -erroff=E_ASSIGNMENT_TYPE_MISMATCH - -CERRWARN += -_gcc=-Wno-implicit-function-declaration +CPPFLAGS += -I$(SRCDIR) -DNOT_HAVE_SA_LEN -D_XPG4_2 -D__EXTENSIONS__ .PARALLEL = $(OBJECTS) .KEEP_STATE: diff --git a/usr/src/lib/libdns_sd/README b/usr/src/lib/libdns_sd/README index a7deae9108..81491ffd91 100644 --- a/usr/src/lib/libdns_sd/README +++ b/usr/src/lib/libdns_sd/README @@ -21,8 +21,9 @@ # # CDDL HEADER END # -#ident "%Z%%M% %I% %E% SMI" +# Copyright 2016 Toomas Soome <tsoome@me.com> # +Updated from upstream version mDNSResponder-576.30.4 Multicast DNS and Service Discovery support in Solaris using the Apple Bonjour source code (v107.6). Apple Bonjour source can be diff --git a/usr/src/lib/libdns_sd/THIRDPARTYLICENSE b/usr/src/lib/libdns_sd/THIRDPARTYLICENSE index b104827415..f4589044ee 100644 --- a/usr/src/lib/libdns_sd/THIRDPARTYLICENSE +++ b/usr/src/lib/libdns_sd/THIRDPARTYLICENSE @@ -1,24 +1,13 @@ - * 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. +The majority of the source code in the mDNSResponder project is licensed +under the terms of the Apache License, Version 2.0, available from: + <http://www.apache.org/licenses/LICENSE-2.0> + +To accommodate license compatibility with the widest possible range +of client code licenses, the shared library code, which is linked +at runtime into the same address space as the client using it, is +licensed under the terms of the "Three-Clause BSD License". + +The Linux Name Service Switch code, contributed by National ICT +Australia Ltd (NICTA) is licensed under the terms of the NICTA Public +Software Licence (which is substantially similar to the "Three-Clause +BSD License", with some additional language pertaining to Australian law). diff --git a/usr/src/lib/libdns_sd/common/dns_sd.h b/usr/src/lib/libdns_sd/common/dns_sd.h index 7ffe6da198..3831527313 100644 --- a/usr/src/lib/libdns_sd/common/dns_sd.h +++ b/usr/src/lib/libdns_sd/common/dns_sd.h @@ -1,40 +1,84 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2013 Apple Computer, Inc. All rights reserved. * - * Redistribution and use in source and binary forms, with or without + * 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 + * 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" + +/*! @header DNS Service Discovery + * + * @discussion This section describes the functions, callbacks, and data structures + * that make up the DNS Service Discovery API. + * + * The DNS Service Discovery API is part of Bonjour, Apple's implementation + * of zero-configuration networking (ZEROCONF). + * + * Bonjour allows you to register a network service, such as a + * printer or file server, so that it can be found by name or browsed + * for by service type and domain. Using Bonjour, applications can + * discover what services are available on the network, along with + * all the information -- such as name, IP address, and port -- + * necessary to access a particular service. + * + * In effect, Bonjour combines the functions of a local DNS server and + * AppleTalk. Bonjour allows applications to provide user-friendly printer + * and server browsing, among other things, over standard IP networks. + * This behavior is a result of combining protocols such as multicast and + * DNS to add new functionality to the network (such as multicast DNS). + * + * Bonjour gives applications easy access to services over local IP + * networks without requiring the service or the application to support + * an AppleTalk or a Netbeui stack, and without requiring a DNS server + * for the local network. + */ + +/* _DNS_SD_H contains the API version number for this header file + * The API version defined in this header file symbol allows for compile-time + * checking, so that C code building with earlier versions of the header file + * can avoid compile errors trying to use functions that aren't even defined + * in those earlier versions. Similar checks may also be performed at run-time: + * => weak linking -- to avoid link failures if run with an earlier + * version of the library that's missing some desired symbol, or + * => DNSServiceGetProperty(DaemonVersion) -- to verify whether the running daemon + * ("system service" on Windows) meets some required minimum functionality level. + */ #ifndef _DNS_SD_H -#define _DNS_SD_H +#define _DNS_SD_H 5763004 #ifdef __cplusplus - extern "C" { +extern "C" { #endif +/* Set to 1 if libdispatch is supported + * Note: May also be set by project and/or Makefile + */ +#ifndef _DNS_SD_LIBDISPATCH +#define _DNS_SD_LIBDISPATCH 0 +#endif /* ndef _DNS_SD_LIBDISPATCH */ + /* standard calling convention under Win32 is __stdcall */ /* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ /* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ @@ -53,32 +97,36 @@ #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; - +#elif defined(EFI32) || defined(EFI64) || defined(EFIX64) +#include "Tiano.h" +#if !defined(_STDINT_H_) +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; +#endif /* Windows has its own differences */ #elif defined(_WIN32) #include <windows.h> #define _UNUSED -#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; +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 + +#if _DNS_SD_LIBDISPATCH +#include <dispatch/dispatch.h> #endif /* DNSServiceRef, DNSRecordRef @@ -91,17 +139,41 @@ typedef INT32 int32_t; typedef struct _DNSServiceRef_t *DNSServiceRef; typedef struct _DNSRecordRef_t *DNSRecordRef; -/* General flags used in functions defined below */ +struct sockaddr; + +/*! @enum General flags + * Most DNS-SD API functions and callbacks include a DNSServiceFlags parameter. + * As a general rule, any given bit in the 32-bit flags field has a specific fixed meaning, + * regardless of the function or callback being used. For any given function or callback, + * typically only a subset of the possible flags are meaningful, and all others should be zero. + * The discussion section for each API call describes which flags are valid for that call + * and callback. In some cases, for a particular call, it may be that no flags are currently + * defined, in which case the DNSServiceFlags parameter exists purely to allow future expansion. + * In all cases, developers should expect that in future releases, it is possible that new flag + * values will be defined, and write code with this in mind. For example, code that tests + * if (flags == kDNSServiceFlagsAdd) ... + * will fail if, in a future release, another bit in the 32-bit flags field is also set. + * The reliable way to test whether a particular bit is set is not with an equality test, + * but with a bitwise mask: + * if (flags & kDNSServiceFlagsAdd) ... + * With the exception of kDNSServiceFlagsValidate, each flag can be valid(be set) + * EITHER only as an input to one of the DNSService*() APIs OR only as an output + * (provide status) through any of the callbacks used. For example, kDNSServiceFlagsAdd + * can be set only as an output in the callback, whereas the kDNSServiceFlagsIncludeP2P + * can be set only as an input to the DNSService*() APIs. See comments on kDNSServiceFlagsValidate + * defined in enum 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 the MoreComing flag is set, applications should not immediately + * update their UI, because this can result in a great deal of ugly flickering + * on the screen, and can waste a great deal of CPU time repeatedly updating + * the screen with content that is then immediately erased, over and over. + * Applications should wait until MoreComing is not set, and then + * update their UI when no more changes are imminent. * When MoreComing is not set, that doesn't mean there will be no more * answers EVER, just that there are no more answers immediately * available right now at this instant. If more answers become available @@ -112,7 +184,7 @@ enum 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" + * conjunction with "Add". An enumeration callback with the "Add" * flag NOT set indicates a "Remove", i.e. the domain is no longer * valid. */ @@ -120,8 +192,8 @@ enum 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 + * 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.) */ @@ -129,8 +201,8 @@ enum 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 + * 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). */ @@ -150,15 +222,308 @@ enum */ 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. + /* Flag for signifying that a query or registration should be performed exclusively via multicast + * DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS. + */ + + kDNSServiceFlagsForce = 0x800, // This flag is deprecated. + + kDNSServiceFlagsKnownUnique = 0x800, + /* + * Client guarantees that record names are unique, so we can skip sending out initial + * probe messages. Standard name conflict resolution is still done if a conflict is discovered. + * Currently only valid for a DNSServiceRegister call. + */ + + kDNSServiceFlagsReturnIntermediates = 0x1000, + /* Flag for returning intermediate results. + * For example, if a query results in an authoritative NXDomain (name does not exist) + * then that result is returned to the client. However the query is not implicitly + * cancelled -- it remains active and if the answer subsequently changes + * (e.g. because a VPN tunnel is subsequently established) then that positive + * result will still be returned to the client. + * Similarly, if a query results in a CNAME record, then in addition to following + * the CNAME referral, the intermediate CNAME result is also returned to the client. + * When this flag is not set, NXDomain errors are not returned, and CNAME records + * are followed silently without informing the client of the intermediate steps. + * (In earlier builds this flag was briefly calledkDNSServiceFlagsReturnCNAME) + */ + + kDNSServiceFlagsNonBrowsable = 0x2000, + /* A service registered with the NonBrowsable flag set can be resolved using + * DNSServiceResolve(), but will not be discoverable using DNSServiceBrowse(). + * This is for cases where the name is actually a GUID; it is found by other means; + * there is no end-user benefit to browsing to find a long list of opaque GUIDs. + * Using the NonBrowsable flag creates SRV+TXT without the cost of also advertising + * an associated PTR record. + */ + + kDNSServiceFlagsShareConnection = 0x4000, + /* For efficiency, clients that perform many concurrent operations may want to use a + * single Unix Domain Socket connection with the background daemon, instead of having a + * separate connection for each independent operation. To use this mode, clients first + * call DNSServiceCreateConnection(&MainRef) to initialize the main DNSServiceRef. + * For each subsequent operation that is to share that same connection, the client copies + * the MainRef, and then passes the address of that copy, setting the ShareConnection flag + * to tell the library that this DNSServiceRef is not a typical uninitialized DNSServiceRef; + * it's a copy of an existing DNSServiceRef whose connection information should be reused. + * + * For example: + * + * DNSServiceErrorType error; + * DNSServiceRef MainRef; + * error = DNSServiceCreateConnection(&MainRef); + * if (error) ... + * DNSServiceRef BrowseRef = MainRef; // Important: COPY the primary DNSServiceRef first... + * error = DNSServiceBrowse(&BrowseRef, kDNSServiceFlagsShareConnection, ...); // then use the copy + * if (error) ... + * ... + * DNSServiceRefDeallocate(BrowseRef); // Terminate the browse operation + * DNSServiceRefDeallocate(MainRef); // Terminate the shared connection + * Also see Point 4.(Don't Double-Deallocate if the MainRef has been Deallocated) in Notes below: + * + * Notes: + * + * 1. Collective kDNSServiceFlagsMoreComing flag + * When callbacks are invoked using a shared DNSServiceRef, the + * kDNSServiceFlagsMoreComing flag applies collectively to *all* active + * operations sharing the same parent DNSServiceRef. If the MoreComing flag is + * set it means that there are more results queued on this parent DNSServiceRef, + * but not necessarily more results for this particular callback function. + * The implication of this for client programmers is that when a callback + * is invoked with the MoreComing flag set, the code should update its + * internal data structures with the new result, and set a variable indicating + * that its UI needs to be updated. Then, later when a callback is eventually + * invoked with the MoreComing flag not set, the code should update *all* + * stale UI elements related to that shared parent DNSServiceRef that need + * updating, not just the UI elements related to the particular callback + * that happened to be the last one to be invoked. + * + * 2. Canceling operations and kDNSServiceFlagsMoreComing + * Whenever you cancel any operation for which you had deferred UI updates + * waiting because of a kDNSServiceFlagsMoreComing flag, you should perform + * those deferred UI updates. This is because, after cancelling the operation, + * you can no longer wait for a callback *without* MoreComing set, to tell + * you do perform your deferred UI updates (the operation has been canceled, + * so there will be no more callbacks). An implication of the collective + * kDNSServiceFlagsMoreComing flag for shared connections is that this + * guideline applies more broadly -- any time you cancel an operation on + * a shared connection, you should perform all deferred UI updates for all + * operations sharing that connection. This is because the MoreComing flag + * might have been referring to events coming for the operation you canceled, + * which will now not be coming because the operation has been canceled. + * + * 3. Only share DNSServiceRef's created with DNSServiceCreateConnection + * Calling DNSServiceCreateConnection(&ref) creates a special shareable DNSServiceRef. + * DNSServiceRef's created by other calls like DNSServiceBrowse() or DNSServiceResolve() + * cannot be shared by copying them and using kDNSServiceFlagsShareConnection. + * + * 4. Don't Double-Deallocate if the MainRef has been Deallocated + * Calling DNSServiceRefDeallocate(ref) for a particular operation's DNSServiceRef terminates + * just that operation. Calling DNSServiceRefDeallocate(ref) for the main shared DNSServiceRef + * (the parent DNSServiceRef, originally created by DNSServiceCreateConnection(&ref)) + * automatically terminates the shared connection and all operations that were still using it. + * After doing this, DO NOT then attempt to deallocate any remaining subordinate DNSServiceRef's. + * The memory used by those subordinate DNSServiceRef's has already been freed, so any attempt + * to do a DNSServiceRefDeallocate (or any other operation) on them will result in accesses + * to freed memory, leading to crashes or other equally undesirable results. + * + * 5. Thread Safety + * The dns_sd.h API does not presuppose any particular threading model, and consequently + * does no locking of its own (which would require linking some specific threading library). + * If client code calls API routines on the same DNSServiceRef concurrently + * from multiple threads, it is the client's responsibility to use a mutext + * lock or take similar appropriate precautions to serialize those calls. + */ + + kDNSServiceFlagsSuppressUnusable = 0x8000, + /* + * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the + * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name) + * but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses + * for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly, + * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for + * "hostname". + */ + + kDNSServiceFlagsTimeout = 0x10000, + /* + * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is + * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped + * is determined by the system and cannot be configured by the user. The query will be stopped irrespective + * of whether a response was given earlier or not. When the query is stopped, the callback will be called + * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo + * and zero length rdata will be returned for DNSServiceQueryRecord. + */ + + kDNSServiceFlagsIncludeP2P = 0x20000, + /* + * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified. + * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces. + */ + + kDNSServiceFlagsWakeOnResolve = 0x40000, + /* + * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet + * to wake up the client. + */ + + kDNSServiceFlagsBackgroundTrafficClass = 0x80000, + /* + * This flag is meaningful for Unicast DNS queries. When set, it uses the background traffic + * class for packets that service the request. + */ + + kDNSServiceFlagsIncludeAWDL = 0x100000, + /* + * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified. + */ + + kDNSServiceFlagsValidate = 0x200000, + /* + * This flag is meaningful in DNSServiceGetAddrInfo and DNSServiceQueryRecord. This is the ONLY flag to be valid + * as an input to the APIs and also an output through the callbacks in the APIs. + * + * When this flag is passed to DNSServiceQueryRecord and DNSServiceGetAddrInfo to resolve unicast names, + * the response will be validated using DNSSEC. The validation results are delivered using the flags field in + * the callback and kDNSServiceFlagsValidate is marked in the flags to indicate that DNSSEC status is also available. + * When the callback is called to deliver the query results, the validation results may or may not be available. + * If it is not delivered along with the results, the validation status is delivered when the validation completes. + * + * When the validation results are delivered in the callback, it is indicated by marking the flags with + * kDNSServiceFlagsValidate and kDNSServiceFlagsAdd along with the DNSSEC status flags (described below) and a NULL + * sockaddr will be returned for DNSServiceGetAddrInfo and zero length rdata will be returned for DNSServiceQueryRecord. + * DNSSEC validation results are for the whole RRSet and not just individual records delivered in the callback. When + * kDNSServiceFlagsAdd is not set in the flags, applications should implicitly assume that the DNSSEC status of the + * RRSet that has been delivered up until that point is not valid anymore, till another callback is called with + * kDNSServiceFlagsAdd and kDNSServiceFlagsValidate. + * + * The following four flags indicate the status of the DNSSEC validation and marked in the flags field of the callback. + * When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the + * other applicable output flags should be masked. See kDNSServiceOutputFlags below. + */ + + kDNSServiceFlagsSecure = 0x200010, + /* + * The response has been validated by verifying all the signaures in the response and was able to + * build a successful authentication chain starting from a known trust anchor. + */ + + kDNSServiceFlagsInsecure = 0x200020, + /* + * A chain of trust cannot be built starting from a known trust anchor to the response. + */ + + kDNSServiceFlagsBogus = 0x200040, + /* + * If the response cannot be verified to be secure due to expired signatures, missing signatures etc., + * then the results are considered to be bogus. + */ + + kDNSServiceFlagsIndeterminate = 0x200080, + /* + * There is no valid trust anchor that can be used to determine whether a response is secure or not. + */ + + kDNSServiceFlagsUnicastResponse = 0x400000, + /* + * Request unicast response to query. + */ + kDNSServiceFlagsValidateOptional = 0x800000, + + /* + * This flag is identical to kDNSServiceFlagsValidate except for the case where the response + * cannot be validated. If this flag is set in DNSServiceQueryRecord or DNSServiceGetAddrInfo, + * the DNSSEC records will be requested for validation. If they cannot be received for some reason + * during the validation (e.g., zone is not signed, zone is signed but cannot be traced back to + * root, recursive server does not understand DNSSEC etc.), then this will fallback to the default + * behavior where the validation will not be performed and no DNSSEC results will be provided. + * + * If the zone is signed and there is a valid path to a known trust anchor configured in the system + * and the application requires DNSSEC validation irrespective of the DNSSEC awareness in the current + * network, then this option MUST not be used. This is only intended to be used during the transition + * period where the different nodes participating in the DNS resolution may not understand DNSSEC or + * managed properly (e.g. missing DS record) but still want to be able to resolve DNS successfully. + */ + + kDNSServiceFlagsWakeOnlyService = 0x1000000, + /* + * This flag is meaningful only in DNSServiceRegister. When set, the service will not be registered + * with sleep proxy server during sleep. + */ + + kDNSServiceFlagsThresholdOne = 0x2000000, + kDNSServiceFlagsThresholdFinder = 0x4000000, + kDNSServiceFlagsThresholdReached = kDNSServiceFlagsThresholdOne, + /* + * kDNSServiceFlagsThresholdOne is meaningful only in DNSServiceBrowse. When set, + * the system will stop issuing browse queries on the network once the number + * of answers returned is one or more. It will issue queries on the network + * again if the number of answers drops to zero. + * This flag is for Apple internal use only. Third party developers + * should not rely on this behavior being supported in any given software release. + * + * kDNSServiceFlagsThresholdFinder is meaningful only in DNSServiceBrowse. When set, + * the system will stop issuing browse queries on the network once the number + * of answers has reached the threshold set for Finder. + * It will issue queries on the network again if the number of answers drops below + * this threshold. + * This flag is for Apple internal use only. Third party developers + * should not rely on this behavior being supported in any given software release. + * + * When kDNSServiceFlagsThresholdReached is set in the client callback add or remove event, + * it indicates that the browse answer threshold has been reached and no + * browse requests will be generated on the network until the number of answers falls + * below the threshold value. Add and remove events can still occur based + * on incoming Bonjour traffic observed by the system. + * The set of services return to the client is not guaranteed to represent the + * entire set of services present on the network once the threshold has been reached. + * + * Note, while kDNSServiceFlagsThresholdReached and kDNSServiceFlagsThresholdOne + * have the same value, there isn't a conflict because kDNSServiceFlagsThresholdReached + * is only set in the callbacks and kDNSServiceFlagsThresholdOne is only set on + * input to a DNSServiceBrowse call. + */ + kDNSServiceFlagsDenyCellular = 0x8000000, + /* + * This flag is meaningful only for Unicast DNS queries. When set, the kernel will restrict + * DNS resolutions on the cellular interface for that request. + */ + + kDNSServiceFlagsServiceIndex = 0x10000000, + /* + * This flag is meaningful only for DNSServiceGetAddrInfo() for Unicast DNS queries. + * When set, DNSServiceGetAddrInfo() will interpret the "interfaceIndex" argument of the call + * as the "serviceIndex". */ - - 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. + + kDNSServiceFlagsDenyExpensive = 0x20000000 + /* + * This flag is meaningful only for Unicast DNS queries. When set, the kernel will restrict + * DNS resolutions on interfaces defined as expensive for that request. */ - }; + +}; + +#define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault) + /* All the output flags excluding the DNSSEC Status flags. Typically used to check DNSSEC Status */ + +/* Possible protocol values */ +enum +{ + /* for DNSServiceGetAddrInfo() */ + kDNSServiceProtocol_IPv4 = 0x01, + kDNSServiceProtocol_IPv6 = 0x02, + /* 0x04 and 0x08 reserved for future internetwork protocols */ + + /* for DNSServiceNATPortMappingCreate() */ + kDNSServiceProtocol_UDP = 0x10, + kDNSServiceProtocol_TCP = 0x20 + /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960] + * or DCCP [RFC 4340]. If future NAT gateways are created that support port + * mappings for these protocols, new constants will be defined here. + */ +}; /* * The values for DNS Classes and Types are listed in RFC 1035, and are available @@ -172,93 +537,120 @@ enum */ 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. */ - }; - +{ + kDNSServiceType_A = 1, /* Host address. */ + kDNSServiceType_NS = 2, /* Authoritative server. */ + kDNSServiceType_MD = 3, /* Mail destination. */ + kDNSServiceType_MF = 4, /* Mail forwarder. */ + kDNSServiceType_CNAME = 5, /* Canonical name. */ + kDNSServiceType_SOA = 6, /* Start of authority zone. */ + kDNSServiceType_MB = 7, /* Mailbox domain name. */ + kDNSServiceType_MG = 8, /* Mail group member. */ + kDNSServiceType_MR = 9, /* Mail rename name. */ + kDNSServiceType_NULL = 10, /* Null resource record. */ + kDNSServiceType_WKS = 11, /* Well known service. */ + kDNSServiceType_PTR = 12, /* Domain name pointer. */ + kDNSServiceType_HINFO = 13, /* Host information. */ + kDNSServiceType_MINFO = 14, /* Mailbox information. */ + kDNSServiceType_MX = 15, /* Mail routing information. */ + kDNSServiceType_TXT = 16, /* One or more text strings (NOT "zero or more..."). */ + kDNSServiceType_RP = 17, /* Responsible person. */ + kDNSServiceType_AFSDB = 18, /* AFS cell database. */ + kDNSServiceType_X25 = 19, /* X_25 calling address. */ + kDNSServiceType_ISDN = 20, /* ISDN calling address. */ + kDNSServiceType_RT = 21, /* Router. */ + kDNSServiceType_NSAP = 22, /* NSAP address. */ + kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ + kDNSServiceType_SIG = 24, /* Security signature. */ + kDNSServiceType_KEY = 25, /* Security key. */ + kDNSServiceType_PX = 26, /* X.400 mail mapping. */ + kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ + kDNSServiceType_AAAA = 28, /* IPv6 Address. */ + kDNSServiceType_LOC = 29, /* Location Information. */ + kDNSServiceType_NXT = 30, /* Next domain (security). */ + kDNSServiceType_EID = 31, /* Endpoint identifier. */ + kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ + kDNSServiceType_SRV = 33, /* Server Selection. */ + kDNSServiceType_ATMA = 34, /* ATM Address */ + kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ + kDNSServiceType_KX = 36, /* Key Exchange */ + kDNSServiceType_CERT = 37, /* Certification record */ + kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */ + kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ + kDNSServiceType_SINK = 40, /* Kitchen sink (experimental) */ + kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ + kDNSServiceType_APL = 42, /* Address Prefix List */ + kDNSServiceType_DS = 43, /* Delegation Signer */ + kDNSServiceType_SSHFP = 44, /* SSH Key Fingerprint */ + kDNSServiceType_IPSECKEY = 45, /* IPSECKEY */ + kDNSServiceType_RRSIG = 46, /* RRSIG */ + kDNSServiceType_NSEC = 47, /* Denial of Existence */ + kDNSServiceType_DNSKEY = 48, /* DNSKEY */ + kDNSServiceType_DHCID = 49, /* DHCP Client Identifier */ + kDNSServiceType_NSEC3 = 50, /* Hashed Authenticated Denial of Existence */ + kDNSServiceType_NSEC3PARAM = 51, /* Hashed Authenticated Denial of Existence */ + + kDNSServiceType_HIP = 55, /* Host Identity Protocol */ + + kDNSServiceType_SPF = 99, /* Sender Policy Framework for E-Mail */ + kDNSServiceType_UINFO = 100, /* IANA-Reserved */ + kDNSServiceType_UID = 101, /* IANA-Reserved */ + kDNSServiceType_GID = 102, /* IANA-Reserved */ + kDNSServiceType_UNSPEC = 103, /* IANA-Reserved */ + + kDNSServiceType_TKEY = 249, /* Transaction key */ + kDNSServiceType_TSIG = 250, /* Transaction signature. */ + kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ + kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ + kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ + kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ + kDNSServiceType_ANY = 255 /* Wildcard match. */ +}; /* possible error code values */ enum - { - kDNSServiceErr_NoError = 0, - kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ - kDNSServiceErr_NoSuchName = -65538, - kDNSServiceErr_NoMemory = -65539, - kDNSServiceErr_BadParam = -65540, - kDNSServiceErr_BadReference = -65541, - kDNSServiceErr_BadState = -65542, - kDNSServiceErr_BadFlags = -65543, - kDNSServiceErr_Unsupported = -65544, - kDNSServiceErr_NotInitialized = -65545, - kDNSServiceErr_AlreadyRegistered = -65547, - kDNSServiceErr_NameConflict = -65548, - kDNSServiceErr_Invalid = -65549, - kDNSServiceErr_Firewall = -65550, - kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ - kDNSServiceErr_BadInterfaceIndex = -65552, - kDNSServiceErr_Refused = -65553, - kDNSServiceErr_NoSuchRecord = -65554, - kDNSServiceErr_NoAuth = -65555, - kDNSServiceErr_NoSuchKey = -65556, - kDNSServiceErr_NATTraversal = -65557, - kDNSServiceErr_DoubleNAT = -65558, - kDNSServiceErr_BadTime = -65559 - /* mDNS Error codes are in the range - * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ - }; - +{ + kDNSServiceErr_NoError = 0, + kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ + kDNSServiceErr_NoSuchName = -65538, + kDNSServiceErr_NoMemory = -65539, + kDNSServiceErr_BadParam = -65540, + kDNSServiceErr_BadReference = -65541, + kDNSServiceErr_BadState = -65542, + kDNSServiceErr_BadFlags = -65543, + kDNSServiceErr_Unsupported = -65544, + kDNSServiceErr_NotInitialized = -65545, + kDNSServiceErr_AlreadyRegistered = -65547, + kDNSServiceErr_NameConflict = -65548, + kDNSServiceErr_Invalid = -65549, + kDNSServiceErr_Firewall = -65550, + kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ + kDNSServiceErr_BadInterfaceIndex = -65552, + kDNSServiceErr_Refused = -65553, + kDNSServiceErr_NoSuchRecord = -65554, + kDNSServiceErr_NoAuth = -65555, + kDNSServiceErr_NoSuchKey = -65556, + kDNSServiceErr_NATTraversal = -65557, + kDNSServiceErr_DoubleNAT = -65558, + kDNSServiceErr_BadTime = -65559, /* Codes up to here existed in Tiger */ + kDNSServiceErr_BadSig = -65560, + kDNSServiceErr_BadKey = -65561, + kDNSServiceErr_Transient = -65562, + kDNSServiceErr_ServiceNotRunning = -65563, /* Background daemon not running */ + kDNSServiceErr_NATPortMappingUnsupported = -65564, /* NAT doesn't support PCP, NAT-PMP or UPnP */ + kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator */ + kDNSServiceErr_NoRouter = -65566, /* No router currently configured (probably no network connectivity) */ + kDNSServiceErr_PollingMode = -65567, + kDNSServiceErr_Timeout = -65568 + + /* mDNS Error codes are in the range + * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ +}; /* Maximum length, in bytes, of a service name represented as a */ /* literal C-String, including the terminating NULL at the end. */ @@ -268,15 +660,15 @@ enum /* 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 +#define kDNSServiceMaxDomainName 1009 /* * Notes on DNS Name Escaping * -- or -- - * "Why is kDNSServiceMaxDomainName 1005, when the maximum legal domain name is 255 bytes?" + * "Why is kDNSServiceMaxDomainName 1009, when the maximum legal domain name is 256 bytes?" * - * All strings used in DNS-SD are UTF-8 strings. - * With few exceptions, most are also escaped using standard DNS escaping rules: + * All strings used in the DNS-SD APIs are UTF-8 strings. Apart from the exceptions noted below, + * the APIs expect the strings to be properly escaped, using the conventional DNS escaping rules: * * '\\' represents a single literal '\' in the name * '\.' represents a single literal '.' in the name @@ -298,10 +690,10 @@ enum * * 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 + * _service._udp, where the "service" part is 1-15 characters, which may be * letters, digits, or hyphens. The domain part of the three-part name may be * any legal domain, providing that the resulting servicename+regtype+domain - * name does not exceed 255 bytes. + * name does not exceed 256 bytes. * * For most software, these issues are transparent. When browsing, the discovered * servicenames should simply be displayed as-is. When resolving, the discovered @@ -319,23 +711,23 @@ enum */ -/* +/* * 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 @@ -344,46 +736,148 @@ enum * running on the same machine, this allows the client to advertise that service * in a way such that it does not inadvertently appear in service lists on * all the other machines on the network. - * + * * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing * then it will find *all* records registered on that same local machine. * Clients explicitly wishing to discover *only* LocalOnly services can * accomplish this by inspecting the interfaceIndex of each service reported * to their DNSServiceBrowseReply() callback function, and discarding those * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. + * + * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, Register, + * and Resolve operations. It should not be used in other DNSService APIs. + * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or + * DNSServiceQueryRecord, it restricts the operation to P2P. + * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceRegister, it is + * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P + * set. + * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is + * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P + * set, because resolving a P2P service may create and/or enable an interface whose + * index is not known a priori. The resolve callback will indicate the index of the + * interface via which the service can be accessed. + * + * If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse + * or DNSServiceQueryRecord, they must set the kDNSServiceFlagsIncludeP2P flag + * to include P2P. In this case, if a service instance or the record being queried + * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P + * as the interface index. */ #define kDNSServiceInterfaceIndexAny 0 -#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) -1 ) - +#define kDNSServiceInterfaceIndexLocalOnly ((uint32_t)-1) +#define kDNSServiceInterfaceIndexUnicast ((uint32_t)-2) +#define kDNSServiceInterfaceIndexP2P ((uint32_t)-3) typedef uint32_t DNSServiceFlags; +typedef uint32_t DNSServiceProtocol; typedef int32_t DNSServiceErrorType; /********************************************************************************************* +* +* Version checking +* +*********************************************************************************************/ + +/* DNSServiceGetProperty() Parameters: * - * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions + * property: The requested property. + * Currently the only property defined is kDNSServiceProperty_DaemonVersion. * - *********************************************************************************************/ + * result: Place to store result. + * For retrieving DaemonVersion, this should be the address of a uint32_t. + * + * size: Pointer to uint32_t containing size of the result location. + * For retrieving DaemonVersion, this should be sizeof(uint32_t). + * On return the uint32_t is updated to the size of the data returned. + * For DaemonVersion, the returned size is always sizeof(uint32_t), but + * future properties could be defined which return variable-sized results. + * + * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning + * if the daemon (or "system service" on Windows) is not running. + */ + +DNSServiceErrorType DNSSD_API DNSServiceGetProperty +( + const char *property, /* Requested property (i.e. kDNSServiceProperty_DaemonVersion) */ + void *result, /* Pointer to place to store result */ + uint32_t *size /* size of result location */ +); + +/* + * When requesting kDNSServiceProperty_DaemonVersion, the result pointer must point + * to a 32-bit unsigned integer, and the size parameter must be set to sizeof(uint32_t). + * + * On return, the 32-bit unsigned integer contains the API version number + * + * For example, Mac OS X 10.4.9 has API version 1080400. + * This allows applications to do simple greater-than and less-than comparisons: + * e.g. an application that requires at least API version 1080400 can check: + * if (version >= 1080400) ... + * + * Example usage: + * uint32_t version; + * uint32_t size = sizeof(version); + * DNSServiceErrorType err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &version, &size); + * if (!err) printf("DNS_SD API version is %d.%d\n", version / 10000, version / 100 % 100); + */ +#define kDNSServiceProperty_DaemonVersion "DaemonVersion" + + +// Map the source port of the local UDP socket that was opened for sending the DNS query +// to the process ID of the application that triggered the DNS resolution. +// +/* DNSServiceGetPID() Parameters: + * + * srcport: Source port (in network byte order) of the UDP socket that was created by + * the daemon to send the DNS query on the wire. + * + * pid: Process ID of the application that started the name resolution which triggered + * the daemon to send the query on the wire. The value can be -1 if the srcport + * cannot be mapped. + * + * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning + * if the daemon is not running. The value of the pid is undefined if the return + * value has error. + */ +DNSServiceErrorType DNSSD_API DNSServiceGetPID +( + uint16_t srcport, + int32_t *pid +); + +/********************************************************************************************* +* +* 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. + * The DNS Service Discovery implementation uses this socket to communicate between the client and + * the daemon. The application MUST NOT directly read from or write to this socket. + * Access to the socket is provided so that it can be used as a kqueue event source, a CFRunLoop + * event source, in a select() loop, etc. When the underlying event management subsystem (kqueue/ + * select/CFRunLoop etc.) indicates to the client that data is available for reading on the + * socket, the client should call DNSServiceProcessResult(), which will extract the daemon's + * reply from the socket, and pass it to the appropriate application callback. By using a run + * loop or select(), results from the daemon can be processed asynchronously. Alternatively, + * a client can choose to fork a thread and have it loop calling "DNSServiceProcessResult(ref);" + * If DNSServiceProcessResult() is called when no data is available for reading on the socket, it + * will block until data does become available, and then process the data and return to the caller. + * The application is reponsible for checking the return value of DNSServiceProcessResult() to determine + * if the socket is valid and if it should continue to process data on the socket. + * When data arrives on the socket, the client is responsible for calling DNSServiceProcessResult(ref) + * in a timely fashion -- if the client allows a large backlog of data to build up the daemon + * may terminate the connection. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. * * return value: The DNSServiceRef's underlying socket descriptor, or -1 on * error. @@ -394,11 +888,11 @@ 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 + * 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 + * 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. @@ -425,15 +919,13 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); * * 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, + * 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. + * Note: This call is to be used only with the DNSServiceRef defined by this API. * * sdRef: A DNSServiceRef initialized by any of the DNSService calls. * @@ -443,10 +935,10 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); /********************************************************************************************* - * - * Domain Enumeration - * - *********************************************************************************************/ +* +* Domain Enumeration +* +*********************************************************************************************/ /* DNSServiceEnumerateDomains() * @@ -471,7 +963,7 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); * kDNSServiceFlagsAdd * kDNSServiceFlagsDefault * - * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given + * 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 @@ -484,20 +976,19 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); */ typedef void (DNSSD_API *DNSServiceDomainEnumReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, +( + 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 + * 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(). @@ -509,7 +1000,7 @@ typedef void (DNSSD_API *DNSServiceDomainEnumReply) * * 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 + * 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 @@ -518,36 +1009,44 @@ typedef void (DNSSD_API *DNSServiceDomainEnumReply) * 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 + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous * errors are delivered to the callback), otherwise returns an error code indicating * the error that occurred (the callback is not invoked and the DNSServiceRef - * is not initialized.) + * is not initialized). */ DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceDomainEnumReply callBack, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, void *context /* may be NULL */ - ); +); /********************************************************************************************* - * - * Service Registration - * - *********************************************************************************************/ +* +* 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. + * flags: When a name is successfully registered, the callback will be + * invoked with the kDNSServiceFlagsAdd flag set. When Wide-Area + * DNS-SD is in use, it is possible for a single service to get + * more than one success callback (e.g. one in the "local" multicast + * DNS domain, and another in a wide-area unicast DNS domain). + * If a successfully-registered name later suffers a name conflict + * or similar problem and has to be deregistered, the callback will + * be invoked with the kDNSServiceFlagsAdd flag not set. The callback + * is *not* invoked in the case where the caller explicitly terminates + * the service registration by calling DNSServiceRefDeallocate(ref); * * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will * indicate the failure that occurred (including name conflicts, @@ -568,31 +1067,31 @@ DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains */ typedef void (DNSSD_API *DNSServiceRegisterReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - DNSServiceErrorType errorCode, +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context - ); +); -/* DNSServiceRegister() Parameters: +/* DNSServiceRegister() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * 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 + * 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. + * 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 @@ -604,17 +1103,71 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * * 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. + * by 1-15 characters, which may be letters, digits, or hyphens. * The transport protocol must be "_tcp" or "_udp". New service types * should be registered at <http://www.dns-sd.org/ServiceTypes.html>. * + * Additional subtypes of the primary service type (where a service + * type has defined subtypes) follow the primary service type in a + * comma-separated list, with no additional spaces, e.g. + * "_primarytype._tcp,_subtype1,_subtype2,_subtype3" + * Subtypes provide a mechanism for filtered browsing: A client browsing + * for "_primarytype._tcp" will discover all instances of this type; + * a client browsing for "_primarytype._tcp,_subtype2" will discover only + * those instances that were registered with "_subtype2" in their list of + * registered subtypes. + * + * The subtype mechanism can be illustrated with some examples using the + * dns-sd command-line tool: + * + * % dns-sd -R Simple _test._tcp "" 1001 & + * % dns-sd -R Better _test._tcp,HasFeatureA "" 1002 & + * % dns-sd -R Best _test._tcp,HasFeatureA,HasFeatureB "" 1003 & + * + * Now: + * % dns-sd -B _test._tcp # will find all three services + * % dns-sd -B _test._tcp,HasFeatureA # finds "Better" and "Best" + * % dns-sd -B _test._tcp,HasFeatureB # finds only "Best" + * + * Subtype labels may be up to 63 bytes long, and may contain any eight- + * bit byte values, including zero bytes. However, due to the nature of + * using a C-string-based API, conventional DNS escaping must be used for + * dots ('.'), commas (','), backslashes ('\') and zero bytes, as shown below: + * + * % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123 + * + * When a service is registered, all the clients browsing for the registered + * type ("regtype") will discover it. If the discovery should be + * restricted to a smaller set of well known peers, the service can be + * registered with additional data (group identifier) that is known + * only to a smaller set of peers. The group identifier should follow primary + * service type using a colon (":") as a delimeter. If subtypes are also present, + * it should be given before the subtype as shown below. + * + * % dns-sd -R _test1 _http._tcp:mygroup1 local 1001 + * % dns-sd -R _test2 _http._tcp:mygroup2 local 1001 + * % dns-sd -R _test3 _http._tcp:mygroup3,HasFeatureA local 1001 + * + * Now: + * % dns-sd -B _http._tcp:"mygroup1" # will discover only test1 + * % dns-sd -B _http._tcp:"mygroup2" # will discover only test2 + * % dns-sd -B _http._tcp:"mygroup3",HasFeatureA # will discover only test3 + * + * By specifying the group information, only the members of that group are + * discovered. + * + * The group identifier itself is not sent in clear. Only a hash of the group + * identifier is sent and the clients discover them anonymously. The group identifier + * may be up to 256 bytes long and may contain any eight bit values except comma which + * should be escaped. + * * 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 + * 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 + * 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(). @@ -622,9 +1175,9 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * 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. + * 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. + * 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> ... @@ -637,41 +1190,41 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * 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 + * 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. + * 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 + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous * errors are delivered to the callback), otherwise returns an error code indicating * the error that occurred (the callback is never invoked and the DNSServiceRef - * is not initialized.) + * is not initialized). */ DNSServiceErrorType DNSSD_API DNSServiceRegister - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + 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, + uint16_t port, /* In network byte order */ + uint16_t txtLen, const void *txtRecord, /* may be NULL */ - DNSServiceRegisterReply callBack, /* 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 + * 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(). @@ -682,12 +1235,11 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister * 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 + * 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. @@ -700,32 +1252,33 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister * * 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. + * ttl: The time to live of the resource record, in seconds. + * Most clients should pass 0 to indicate that the system should + * select a sensible default value. * * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an * error code indicating the error that occurred (the RecordRef is not initialized). */ DNSServiceErrorType DNSSD_API DNSServiceAddRecord - ( - DNSServiceRef sdRef, +( + DNSServiceRef sdRef, DNSRecordRef *RecordRef, - DNSServiceFlags flags, - uint16_t rrtype, - uint16_t rdlen, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, const void *rdata, - uint32_t ttl - ); + uint32_t ttl +); /* DNSServiceUpdateRecord * - * Update a registered resource record. The record must either be: + * 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() @@ -741,20 +1294,22 @@ DNSServiceErrorType DNSSD_API DNSServiceAddRecord * rdata: The new rdata to be contained in the updated resource record. * * ttl: The time to live of the updated resource record, in seconds. + * Most clients should pass 0 to indicate that the system should + * select a sensible default value. * * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an * error code indicating the error that occurred. */ DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, /* may be NULL */ - DNSServiceFlags flags, - uint16_t rdlen, +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, /* may be NULL */ + DNSServiceFlags flags, + uint16_t rdlen, const void *rdata, - uint32_t ttl - ); + uint32_t ttl +); /* DNSServiceRemoveRecord @@ -779,22 +1334,21 @@ DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord */ DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, - DNSServiceFlags flags - ); +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags +); /********************************************************************************************* - * - * Service Discovery - * - *********************************************************************************************/ +* +* Service Discovery +* +*********************************************************************************************/ /* Browse for instances of a service. * - * * DNSServiceBrowseReply() Parameters: * * sdRef: The DNSServiceRef initialized by DNSServiceBrowse(). @@ -802,11 +1356,11 @@ DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord * flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd. * See flag definitions for details. * - * interfaceIndex: The interface on which the service is advertised. This index should + * 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 + * 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, @@ -832,21 +1386,21 @@ DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord */ typedef void (DNSSD_API *DNSServiceBrowseReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, +( + 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 + * 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(). @@ -855,11 +1409,18 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) * * 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 + * 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". + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * A client may optionally specify a single subtype to perform filtered browsing: + * e.g. browsing for "_primarytype._tcp,_subtype" will discover only those + * instances of "_primarytype._tcp" that were registered specifying "_subtype" + * in their list of registered subtypes. Additionally, a group identifier may + * also be specified before the subtype e.g., _primarytype._tcp:GroupID, which + * will discover only the members that register the service with GroupID. See + * DNSServiceRegister for more details. * * domain: If non-NULL, specifies the domain on which to browse for services. * Most applications will not specify a domain, instead browsing on the @@ -871,22 +1432,22 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) * 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 + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous * errors are delivered to the callback), otherwise returns an error code indicating * the error that occurred (the callback is not invoked and the DNSServiceRef - * is not initialized.) + * is not initialized). */ DNSServiceErrorType DNSSD_API DNSServiceBrowse - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *regtype, const char *domain, /* may be NULL */ - DNSServiceBrowseReply callBack, + DNSServiceBrowseReply callBack, void *context /* may be NULL */ - ); +); /* DNSServiceResolve() @@ -908,12 +1469,12 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse * * sdRef: The DNSServiceRef initialized by DNSServiceResolve(). * - * flags: Currently unused, reserved for future use. + * flags: Possible values: kDNSServiceFlagsMoreComing * * interfaceIndex: The interface on which the service was resolved. * * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will - * indicate the failure that occurred. Other parameters are undefined if + * 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>. @@ -922,7 +1483,7 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse * 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 + * 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. @@ -951,28 +1512,30 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse */ typedef void (DNSSD_API *DNSServiceResolveReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, - uint16_t port, - uint16_t txtLen, + uint16_t port, /* In network byte order */ + uint16_t txtLen, const unsigned char *txtRecord, void *context - ); +); /* DNSServiceResolve() Parameters * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * 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. + * flags: Specifying kDNSServiceFlagsForceMulticast will cause query to be + * performed with a link-local mDNS query, even if the name is an + * apparently non-local name (i.e. a name not ending in ".local.") * * interfaceIndex: The interface on which to resolve the service. If this resolve call is * as a result of a currently active DNSServiceBrowse() operation, then the @@ -997,40 +1560,257 @@ typedef void (DNSSD_API *DNSServiceResolveReply) * 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 + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous * errors are delivered to the callback), otherwise returns an error code indicating * the error that occurred (the callback is never invoked and the DNSServiceRef - * is not initialized.) + * is not initialized). */ DNSServiceErrorType DNSSD_API DNSServiceResolve - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *name, const char *regtype, const char *domain, - DNSServiceResolveReply callBack, + DNSServiceResolveReply callBack, void *context /* may be NULL */ - ); +); /********************************************************************************************* +* +* Querying Individual Specific Records +* +*********************************************************************************************/ + +/* DNSServiceQueryRecord + * + * Query for an arbitrary DNS record. + * + * DNSServiceQueryRecordReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and + * kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records + * with a ttl of 0, i.e. "Remove" events. + * + * interfaceIndex: The interface on which the query was resolved (the index for a given + * interface is determined via the if_nametoindex() family of calls). + * See "Constants for specifying an interface index" for more details. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * errorCode is nonzero. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. * - * Special Purpose Calls (most applications will not use these) + * ttl: If the client wishes to cache the result for performance reasons, + * the TTL indicates how long the client may legitimately hold onto + * this result, in seconds. After the TTL expires, the client should + * consider the result no longer valid, and if it requires this data + * again, it should be re-fetched with a new query. Of course, this + * only applies to clients that cancel the asynchronous operation when + * they get a result. Clients that leave the asynchronous operation + * running can safely assume that the data remains valid until they + * get another callback telling them otherwise. * - *********************************************************************************************/ + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceQueryRecordReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + void *context +); + + +/* DNSServiceQueryRecord() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the query operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery. + * Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast + * query to a unicast DNS server that implements the protocol. This flag + * has no effect on link-local multicast queries. + * + * interfaceIndex: If non-zero, specifies the interface on which to issue the query + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the name to be queried for on all + * interfaces. See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record to be queried for. + * + * rrtype: The numerical type of the resource record to be queried for + * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized). + */ + +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context /* may be NULL */ +); + + +/********************************************************************************************* +* +* Unified lookup of both IPv4 and IPv6 addresses for a fully qualified hostname +* +*********************************************************************************************/ + +/* DNSServiceGetAddrInfo + * + * Queries for the IP address of a hostname by using either Multicast or Unicast DNS. + * + * DNSServiceGetAddrInfoReply() parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceGetAddrInfo(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and + * kDNSServiceFlagsAdd. + * + * interfaceIndex: The interface to which the answers pertain. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are + * undefined if errorCode is nonzero. + * + * hostname: The fully qualified domain name of the host to be queried for. + * + * address: IPv4 or IPv6 address. + * + * ttl: If the client wishes to cache the result for performance reasons, + * the TTL indicates how long the client may legitimately hold onto + * this result, in seconds. After the TTL expires, the client should + * consider the result no longer valid, and if it requires this data + * again, it should be re-fetched with a new query. Of course, this + * only applies to clients that cancel the asynchronous operation when + * they get a result. Clients that leave the asynchronous operation + * running can safely assume that the data remains valid until they + * get another callback telling them otherwise. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceGetAddrInfoReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *hostname, + const struct sockaddr *address, + uint32_t ttl, + void *context +); + + +/* DNSServiceGetAddrInfo() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it + * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the query + * begins and will last indefinitely until the client terminates the query + * by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: kDNSServiceFlagsForceMulticast + * + * interfaceIndex: The interface on which to issue the query. Passing 0 causes the query to be + * sent on all active interfaces via Multicast or the primary interface via Unicast. + * + * protocol: Pass in kDNSServiceProtocol_IPv4 to look up IPv4 addresses, or kDNSServiceProtocol_IPv6 + * to look up IPv6 addresses, or both to look up both kinds. If neither flag is + * set, the system will apply an intelligent heuristic, which is (currently) + * that it will attempt to look up both, except: + * + * * If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name) + * but this host has no routable IPv6 address, then the call will not try to + * look up IPv6 addresses for "hostname", since any addresses it found would be + * unlikely to be of any use anyway. Similarly, if this host has no routable + * IPv4 address, the call will not try to look up IPv4 addresses for "hostname". + * + * hostname: The fully qualified domain name of the host to be queried for. + * + * callBack: The function to be called when the query succeeds or fails asynchronously. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceProtocol protocol, + const char *hostname, + DNSServiceGetAddrInfoReply callBack, + void *context /* may be NULL */ +); + + +/********************************************************************************************* +* +* Special Purpose Calls: +* DNSServiceCreateConnection(), DNSServiceRegisterRecord(), DNSServiceReconfirmRecord() +* (most applications will not use these) +* +*********************************************************************************************/ /* DNSServiceCreateConnection() * * Create a connection to the daemon allowing efficient registration of * multiple individual records. * - * * Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating * the reference (via DNSServiceRefDeallocate()) severs the * connection and deregisters all records registered on this connection. * @@ -1041,7 +1821,6 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); - /* DNSServiceRegisterRecord * * Register an individual resource record on a connected DNSServiceRef. @@ -1049,13 +1828,12 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); * 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 + * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above * DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is * invalidated, and may not be used further. * @@ -1069,32 +1847,32 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); * */ - typedef void (DNSSD_API *DNSServiceRegisterRecordReply) - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, - DNSServiceFlags flags, - DNSServiceErrorType errorCode, +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 + * 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()). + * DNSServiceRefDeallocate()). * * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique * (see flag type definitions for details). * * interfaceIndex: If non-zero, specifies the interface on which to register the record * (the index for a given interface is determined via the if_nametoindex() - * family of calls.) Passing 0 causes the record to be registered on all interfaces. + * 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. @@ -1107,7 +1885,9 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); * * 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. + * ttl: The time to live of the resource record, in seconds. + * Most clients should pass 0 to indicate that the system should + * select a sensible default value. * * callBack: The function to be called when a result is found, or if the call * asynchronously fails (e.g. because of a name conflict.) @@ -1115,49 +1895,48 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); * 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 + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous * errors are delivered to the callback), otherwise returns an error code indicating * the error that occurred (the callback is never invoked and the DNSRecordRef is - * not initialized.) + * not initialized). */ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord - ( - DNSServiceRef sdRef, +( + DNSServiceRef sdRef, DNSRecordRef *RecordRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, const void *rdata, - uint32_t ttl, - DNSServiceRegisterRecordReply callBack, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, void *context /* may be NULL */ - ); +); -/* DNSServiceQueryRecord - * - * Query for an arbitrary DNS record. - * - * - * DNSServiceQueryRecordReply() Callback Parameters: +/* DNSServiceReconfirmRecord * - * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord(). + * Instruct the daemon to verify the validity of a resource record that appears + * to be out of date (e.g. because TCP connection to a service's target failed.) + * Causes the record to be flushed from the daemon's cache (as well as all other + * daemons' caches on the network) if the record is determined to be invalid. + * Use this routine conservatively. Reconfirming a record necessarily consumes + * network bandwidth, so this should not be done indiscriminately. * - * 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. + * Parameters: * - * 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. + * flags: Not currently used. * - * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will - * indicate the failure that occurred. Other parameters are undefined if - * errorCode is nonzero. + * interfaceIndex: Specifies the interface of the record in question. + * The caller must specify the interface. + * This API (by design) causes increased network traffic, so it requires + * the caller to be precise about which record should be reconfirmed. + * It is not possible to pass zero for the interface index to perform + * a "wildcard" reconfirmation, where *all* matching records are reconfirmed. * * fullname: The resource record's full domain name. * @@ -1169,122 +1948,210 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord * * 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 - ); +DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord +( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata +); -/* DNSServiceQueryRecord() Parameters: +/********************************************************************************************* +* +* NAT Port Mapping +* +*********************************************************************************************/ + +/* DNSServiceNATPortMappingCreate + * + * Request a port mapping in the NAT gateway, which maps a port on the local machine + * to an external port on the NAT. The NAT should support either PCP, NAT-PMP or the + * UPnP/IGD protocol for this API to create a successful mapping. Note that this API + * currently supports IPv4 addresses/mappings only. If the NAT gateway supports PCP and + * returns an IPv6 address (incorrectly, since this API specifically requests IPv4 + * addresses), the DNSServiceNATPortMappingReply callback will be invoked with errorCode + * kDNSServiceErr_NATPortMappingUnsupported. + * + * The port mapping will be renewed indefinitely until the client process exits, or + * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate(). + * The client callback will be invoked, informing the client of the NAT gateway's + * external IP address and the external port that has been allocated for this client. + * The client should then record this external IP address and port using whatever + * directory service mechanism it is using to enable peers to connect to it. + * (Clients advertising services using Wide-Area DNS-SD DO NOT need to use this API + * -- when a client calls DNSServiceRegister() NAT mappings are automatically created + * and the external IP address and port for the service are recorded in the global DNS. + * Only clients using some directory mechanism other than Wide-Area DNS-SD need to use + * this API to explicitly map their own ports.) + * + * It's possible that the client callback could be called multiple times, for example + * if the NAT gateway's IP address changes, or if a configuration change results in a + * different external port being mapped for this client. Over the lifetime of any long-lived + * port mapping, the client should be prepared to handle these notifications of changes + * in the environment, and should update its recorded address and/or port as appropriate. + * + * NOTE: There are two unusual aspects of how the DNSServiceNATPortMappingCreate API works, + * which were intentionally designed to help simplify client code: + * + * 1. It's not an error to request a NAT mapping when the machine is not behind a NAT gateway. + * In other NAT mapping APIs, if you request a NAT mapping and the machine is not behind a NAT + * gateway, then the API returns an error code -- it can't get you a NAT mapping if there's no + * NAT gateway. The DNSServiceNATPortMappingCreate API takes a different view. Working out + * whether or not you need a NAT mapping can be tricky and non-obvious, particularly on + * a machine with multiple active network interfaces. Rather than make every client recreate + * this logic for deciding whether a NAT mapping is required, the PortMapping API does that + * work for you. If the client calls the PortMapping API when the machine already has a + * routable public IP address, then instead of complaining about it and giving an error, + * the PortMapping API just invokes your callback, giving the machine's public address + * and your own port number. This means you don't need to write code to work out whether + * your client needs to call the PortMapping API -- just call it anyway, and if it wasn't + * necessary, no harm is done: + * + * - If the machine already has a routable public IP address, then your callback + * will just be invoked giving your own address and port. + * - If a NAT mapping is required and obtained, then your callback will be invoked + * giving you the external address and port. + * - If a NAT mapping is required but not obtained from the local NAT gateway, + * or the machine has no network connectivity, then your callback will be + * invoked giving zero address and port. + * + * 2. In other NAT mapping APIs, if a laptop computer is put to sleep and woken up on a new + * network, it's the client's job to notice this, and work out whether a NAT mapping + * is required on the new network, and make a new NAT mapping request if necessary. + * The DNSServiceNATPortMappingCreate API does this for you, automatically. + * The client just needs to make one call to the PortMapping API, and its callback will + * be invoked any time the mapping state changes. This property complements point (1) above. + * If the client didn't make a NAT mapping request just because it determined that one was + * not required at that particular moment in time, the client would then have to monitor + * for network state changes to determine if a NAT port mapping later became necessary. + * By unconditionally making a NAT mapping request, even when a NAT mapping not to be + * necessary, the PortMapping API will then begin monitoring network state changes on behalf of + * the client, and if a NAT mapping later becomes necessary, it will automatically create a NAT + * mapping and inform the client with a new callback giving the new address and port information. + * + * DNSServiceNATPortMappingReply() parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceNATPortMappingCreate(). * - * 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: Currently unused, reserved for future use. * - * 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: The interface through which the NAT gateway is reached. * - * 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. + * errorCode: Will be kDNSServiceErr_NoError on success. + * Will be kDNSServiceErr_DoubleNAT when the NAT gateway is itself behind one or + * more layers of NAT, in which case the other parameters have the defined values. + * For other failures, will indicate the failure that occurred, and the other + * parameters are undefined. * - * fullname: The full domain name of the resource record to be queried for. + * externalAddress: Four byte IPv4 address in network byte order. * - * rrtype: The numerical type of the resource record to be queried for - * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * protocol: Will be kDNSServiceProtocol_UDP or kDNSServiceProtocol_TCP or both. * - * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * internalPort: The port on the local machine that was mapped. * - * callBack: The function to be called when a result is found, or if the call - * asynchronously fails. + * externalPort: The actual external port in the NAT gateway that was mapped. + * This is likely to be different than the requested external port. * - * context: An application context pointer which is passed to the callback function - * (may be NULL). + * ttl: The lifetime of the NAT port mapping created on the gateway. + * This controls how quickly stale mappings will be garbage-collected + * if the client machine crashes, suffers a power failure, is disconnected + * from the network, or suffers some other unfortunate demise which + * causes it to vanish without explicitly removing its NAT port mapping. + * It's possible that the ttl value will differ from the requested ttl value. + * + * context: The context pointer that was passed to the callout. * - * 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 */ - ); +typedef void (DNSSD_API *DNSServiceNATPortMappingReply) +( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + uint32_t externalAddress, /* four byte IPv4 address in network byte order */ + DNSServiceProtocol protocol, + uint16_t internalPort, /* In network byte order */ + uint16_t externalPort, /* In network byte order and may be different than the requested port */ + uint32_t ttl, /* may be different than the requested ttl */ + void *context +); -/* DNSServiceReconfirmRecord +/* DNSServiceNATPortMappingCreate() Parameters: * - * 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. + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds then it + * initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the nat + * port mapping will last indefinitely until the client terminates the port + * mapping request by passing this DNSServiceRef to DNSServiceRefDeallocate(). * - * Parameters: + * flags: Currently ignored, reserved for future use. * - * flags: Currently unused, reserved for future use. + * interfaceIndex: The interface on which to create port mappings in a NAT gateway. Passing 0 causes + * the port mapping request to be sent on the primary interface. * - * interfaceIndex: If non-zero, specifies the interface of the record in question. - * Passing 0 causes all instances of this record to be reconfirmed. + * protocol: To request a port mapping, pass in kDNSServiceProtocol_UDP, or kDNSServiceProtocol_TCP, + * or (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP) to map both. + * The local listening port number must also be specified in the internalPort parameter. + * To just discover the NAT gateway's external IP address, pass zero for protocol, + * internalPort, externalPort and ttl. * - * fullname: The resource record's full domain name. + * internalPort: The port number in network byte order on the local machine which is listening for packets. * - * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * externalPort: The requested external port in network byte order in the NAT gateway that you would + * like to map to the internal port. Pass 0 if you don't care which external port is chosen for you. * - * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * ttl: The requested renewal period of the NAT port mapping, in seconds. + * If the client machine crashes, suffers a power failure, is disconnected from + * the network, or suffers some other unfortunate demise which causes it to vanish + * unexpectedly without explicitly removing its NAT port mappings, then the NAT gateway + * will garbage-collect old stale NAT port mappings when their lifetime expires. + * Requesting a short TTL causes such orphaned mappings to be garbage-collected + * more promptly, but consumes system resources and network bandwidth with + * frequent renewal packets to keep the mapping from expiring. + * Requesting a long TTL is more efficient on the network, but in the event of the + * client vanishing, stale NAT port mappings will not be garbage-collected as quickly. + * Most clients should pass 0 to use a system-wide default value. * - * rdlen: The length, in bytes, of the resource record rdata. + * callBack: The function to be called when the port mapping request succeeds or fails asynchronously. * - * rdata: The raw rdata of the resource record. + * context: An application context pointer which is passed to the callback function + * (may be NULL). * + * return value: Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred. + * + * If you don't actually want a port mapped, and are just calling the API + * because you want to find out the NAT's external IP address (e.g. for UI + * display) then pass zero for protocol, internalPort, externalPort and ttl. */ -DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord - ( - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, - const void *rdata - ); +DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceProtocol protocol, /* TCP and/or UDP */ + uint16_t internalPort, /* network byte order */ + uint16_t externalPort, /* network byte order */ + uint32_t ttl, /* time to live in seconds */ + DNSServiceNATPortMappingReply callBack, + void *context /* may be NULL */ +); /********************************************************************************************* - * - * General Utility Functions - * - *********************************************************************************************/ +* +* General Utility Functions +* +*********************************************************************************************/ /* DNSServiceConstructFullName() * @@ -1295,7 +2162,7 @@ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord * 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 + * The buffer must be kDNSServiceMaxDomainName (1009) bytes in length to * accommodate the longest legal domain name without buffer overrun. * * service: The service name - any dots or backslashes must NOT be escaped. @@ -1305,27 +2172,27 @@ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord * 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, + * 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. + * return value: Returns kDNSServiceErr_NoError (0) on success, kDNSServiceErr_BadParam on error. * */ -int DNSSD_API DNSServiceConstructFullName - ( - char *fullName, - const char *service, /* may be NULL */ - const char *regtype, - const char *domain - ); +DNSServiceErrorType DNSSD_API DNSServiceConstructFullName +( + char * const fullName, + const char * const service, /* may be NULL */ + const char * const regtype, + const char * const domain +); /********************************************************************************************* - * - * TXT Record Construction Functions - * - *********************************************************************************************/ +* +* TXT Record Construction Functions +* +*********************************************************************************************/ /* * A typical calling sequence for TXT record construction is something like: @@ -1393,11 +2260,11 @@ typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignmen */ void DNSSD_API TXTRecordCreate - ( +( TXTRecordRef *txtRecord, - uint16_t bufferLen, + uint16_t bufferLen, void *buffer - ); +); /* TXTRecordDeallocate() @@ -1411,9 +2278,9 @@ void DNSSD_API TXTRecordCreate */ void DNSSD_API TXTRecordDeallocate - ( +( TXTRecordRef *txtRecord - ); +); /* TXTRecordSetValue() @@ -1433,7 +2300,7 @@ void DNSSD_API TXTRecordDeallocate * * 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). + * 9 characters or fewer (not counting the terminating null). * * valueSize: The size of the value. * @@ -1454,17 +2321,17 @@ void DNSSD_API TXTRecordDeallocate */ DNSServiceErrorType DNSSD_API TXTRecordSetValue - ( +( TXTRecordRef *txtRecord, const char *key, - uint8_t valueSize, /* may be zero */ + uint8_t valueSize, /* may be zero */ const void *value /* may be NULL */ - ); +); /* TXTRecordRemoveValue() * - * Removes a key from a TXTRecordRef. The "key" must be an + * 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(). @@ -1477,10 +2344,10 @@ DNSServiceErrorType DNSSD_API TXTRecordSetValue */ DNSServiceErrorType DNSSD_API TXTRecordRemoveValue - ( +( TXTRecordRef *txtRecord, const char *key - ); +); /* TXTRecordGetLength() @@ -1496,9 +2363,9 @@ DNSServiceErrorType DNSSD_API TXTRecordRemoveValue */ uint16_t DNSSD_API TXTRecordGetLength - ( +( const TXTRecordRef *txtRecord - ); +); /* TXTRecordGetBytesPtr() @@ -1513,16 +2380,16 @@ uint16_t DNSSD_API TXTRecordGetLength */ const void * DNSSD_API TXTRecordGetBytesPtr - ( +( const TXTRecordRef *txtRecord - ); +); /********************************************************************************************* - * - * TXT Record Parsing Functions - * - *********************************************************************************************/ +* +* TXT Record Parsing Functions +* +*********************************************************************************************/ /* * A typical calling sequence for TXT record parsing is something like: @@ -1532,13 +2399,13 @@ const void * DNSSD_API TXTRecordGetBytesPtr * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1); * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2); * ... - * bcopy(val1ptr, myval1, len1); - * bcopy(val2ptr, myval2, len2); + * memcpy(myval1, val1ptr, len1); + * memcpy(myval2, val2ptr, len2); * ... * return; * * If you wish to retain the values after return from the DNSServiceResolve() - * callback, then you need to copy the data to your own storage using bcopy() + * callback, then you need to copy the data to your own storage using memcpy() * or similar, as shown in the example above. * * If for some reason you need to parse a TXT record you built yourself @@ -1567,11 +2434,11 @@ const void * DNSSD_API TXTRecordGetBytesPtr */ int DNSSD_API TXTRecordContainsKey - ( - uint16_t txtLen, +( + uint16_t txtLen, const void *txtRecord, const char *key - ); +); /* TXTRecordGetValuePtr() @@ -1596,17 +2463,17 @@ int DNSSD_API TXTRecordContainsKey */ const void * DNSSD_API TXTRecordGetValuePtr - ( - uint16_t txtLen, +( + 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 + * 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. @@ -1618,16 +2485,16 @@ const void * DNSSD_API TXTRecordGetValuePtr */ uint16_t DNSSD_API TXTRecordGetCount - ( - uint16_t txtLen, +( + 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. + * 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. @@ -1641,14 +2508,14 @@ uint16_t DNSSD_API TXTRecordGetCount * * txtRecord: Pointer to the received TXT Record bytes. * - * index: An index into the TXT Record. + * itemIndex: An index into the TXT Record. * * keyBufLen: The size of the string buffer being supplied. * * key: A string buffer used to store the key name. * On return, the buffer contains a null-terminated C string * giving the key name. DNS-SD TXT keys are usually - * 8 characters or less. To hold the maximum possible + * 9 characters or fewer. To hold the maximum possible * key name, the buffer should be 256 bytes long. * * valueLen: On output, will be set to the size of the "value" data. @@ -1663,63 +2530,131 @@ uint16_t DNSSD_API TXTRecordGetCount */ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex - ( - uint16_t txtLen, +( + uint16_t txtLen, const void *txtRecord, - uint16_t index, - uint16_t keyBufLen, + uint16_t itemIndex, + uint16_t keyBufLen, char *key, uint8_t *valueLen, const void **value - ); - -#ifdef __APPLE_API_PRIVATE +); +#if _DNS_SD_LIBDISPATCH /* - * Mac OS X specific functionality - * 3rd party clients of this API should not depend on future support or availability of this routine + * DNSServiceSetDispatchQueue + * + * Allows you to schedule a DNSServiceRef on a serial dispatch queue for receiving asynchronous + * callbacks. It's the clients responsibility to ensure that the provided dispatch queue is running. + * + * A typical application that uses CFRunLoopRun or dispatch_main on its main thread will + * usually schedule DNSServiceRefs on its main queue (which is always a serial queue) + * using "DNSServiceSetDispatchQueue(sdref, dispatch_get_main_queue());" + * + * If there is any error during the processing of events, the application callback will + * be called with an error code. For shared connections, each subordinate DNSServiceRef + * will get its own error callback. Currently these error callbacks only happen + * if the daemon is manually terminated or crashes, and the error + * code in this case is kDNSServiceErr_ServiceNotRunning. The application must call + * DNSServiceRefDeallocate to free the DNSServiceRef when it gets such an error code. + * These error callbacks are rare and should not normally happen on customer machines, + * but application code should be written defensively to handle such error callbacks + * gracefully if they occur. + * + * After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult + * on the same DNSServiceRef will result in undefined behavior and should be avoided. + * + * Once the application successfully schedules a DNSServiceRef on a serial dispatch queue using + * DNSServiceSetDispatchQueue, it cannot remove the DNSServiceRef from the dispatch queue, or use + * DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch + * queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until + * the application no longer requires that operation and terminates it using DNSServiceRefDeallocate. + * + * service: DNSServiceRef that was allocated and returned to the application, when the + * application calls one of the DNSService API. + * + * queue: dispatch queue where the application callback will be scheduled + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoMemory if it cannot create a dispatch source + * Returns kDNSServiceErr_BadParam if the service param is invalid or the + * queue param is invalid */ -/* DNSServiceSetDefaultDomainForUser() +DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue +( + DNSServiceRef service, + dispatch_queue_t queue +); +#endif //_DNS_SD_LIBDISPATCH + +#if !defined(_WIN32) +typedef void (DNSSD_API *DNSServiceSleepKeepaliveReply) +( + DNSServiceRef sdRef, + DNSServiceErrorType errorCode, + void *context +); +DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + int fd, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void *context +); +#endif + +#ifdef APPLE_OSX_mDNSResponder +/* DNSServiceCreateDelegateConnection() * - * 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. - * + * Create a delegate connection to the daemon allowing efficient registration of + * multiple individual records. * * Parameters: * - * flags: Pass kDNSServiceFlagsAdd to add a domain for a user. Call without - * this flag set to clear a previously added domain. + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * the reference (via DNSServiceRefDeallocate()) severs the + * connection and deregisters all records registered on this connection. * - * domain: The domain to be used for the caller's UID. + * pid : Process ID of the delegate * - * return value: Returns kDNSServiceErr_NoError on succeses, otherwise returns - * an error code indicating the error that occurred + * uuid: UUID of the delegate + * + * Note that only one of the two arguments (pid or uuid) can be specified. If pid + * is zero, uuid will be assumed to be a valid value; otherwise pid will be used. + * + * 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). kDNSServiceErr_NotAuth is + * returned to indicate that the calling process does not have entitlements + * to use this API. */ +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid); +#endif + +#ifdef __APPLE_API_PRIVATE + +#define kDNSServiceCompPrivateDNS "PrivateDNS" +#define kDNSServiceCompMulticastDNS "MulticastDNS" -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. +/* 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]; - }; +struct CompileTimeAssertionChecks_DNS_SD +{ + 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 index b5a45c8b63..cca5885333 100644 --- a/usr/src/lib/libdns_sd/common/dnssd_clientlib.c +++ b/usr/src/lib/libdns_sd/common/dnssd_clientlib.c @@ -2,74 +2,30 @@ * * Copyright (c) 2004, Apple Computer, Inc. All rights reserved. * - * Redistribution and use in source and binary forms, with or without + * 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. + * 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 + * 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> @@ -82,292 +38,329 @@ like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code #if defined(_WIN32) // disable warning "conversion from <data> to uint16_t" #pragma warning(disable:4244) +#define strncasecmp _strnicmp +#define strcasecmp _stricmp #endif /********************************************************************************************* - * - * Supporting Functions - * - *********************************************************************************************/ +* +* Supporting Functions +* +*********************************************************************************************/ -#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') +#define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9') + +// DomainEndsInDot returns 1 if name ends with a dot, 0 otherwise +// (DNSServiceConstructFullName depends this returning 1 for true, rather than any non-zero value meaning true) static int DomainEndsInDot(const char *dom) - { - while (dom[0] && dom[1]) - { - if (dom[0] == '\\') // advance past escaped byte sequence - { - if (mdnsIsDigit(dom[1]) && mdnsIsDigit(dom[2]) && mdnsIsDigit(dom[3])) - dom += 4; // If "\ddd" then skip four - else dom += 2; // else if "\x" then skip two - } - else dom++; // else goto next character - } - return (dom[0] == '.'); - } +{ + 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); - } +( + uint16_t txtLen, + const void *txtRecord, + const char *key, + unsigned long *keylen +) +{ + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + *keylen = (unsigned long) strlen(key); + while (p<e) + { + uint8_t *x = p; + p += 1 + p[0]; + if (p <= e && *keylen <= x[0] && !strncasecmp(key, (char*)x+1, *keylen)) + if (*keylen == x[0] || x[1+*keylen] == '=') return(x); + } + return(NULL); +} /********************************************************************************************* - * - * General Utility Functions - * - *********************************************************************************************/ - -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; - } +* +* General Utility Functions +* +*********************************************************************************************/ + +// Note: Need to make sure we don't write more than kDNSServiceMaxDomainName (1009) bytes to fullName +// In earlier builds this constant was defined to be 1005, so to avoid buffer overruns on clients +// compiled with that constant we'll actually limit the output to 1005 bytes. + +DNSServiceErrorType DNSSD_API DNSServiceConstructFullName +( + char *const fullName, + const char *const service, // May be NULL + const char *const regtype, + const char *const domain +) +{ + const size_t len = !regtype ? 0 : strlen(regtype) - DomainEndsInDot(regtype); + char *fn = fullName; + char *const lim = fullName + 1005; + const char *s = service; + const char *r = regtype; + const char *d = domain; + + // regtype must be at least "x._udp" or "x._tcp" + if (len < 6 || !domain || !domain[0]) return kDNSServiceErr_BadParam; + if (strncasecmp((regtype + len - 4), "_tcp", 4) && strncasecmp((regtype + len - 4), "_udp", 4)) return kDNSServiceErr_BadParam; + + if (service && *service) + { + while (*s) + { + unsigned char c = *s++; // Needs to be unsigned, or values like 0xFF will be interpreted as < 32 + if (c <= ' ') // Escape non-printable characters + { + if (fn+4 >= lim) goto fail; + *fn++ = '\\'; + *fn++ = '0' + (c / 100); + *fn++ = '0' + (c / 10) % 10; + c = '0' + (c ) % 10; + } + else if (c == '.' || (c == '\\')) // Escape dot and backslash literals + { + if (fn+2 >= lim) goto fail; + *fn++ = '\\'; + } + else + if (fn+1 >= lim) goto fail; + *fn++ = (char)c; + } + *fn++ = '.'; + } + + while (*r) if (fn+1 >= lim) goto fail;else *fn++ = *r++; + if (!DomainEndsInDot(regtype)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';} + + while (*d) if (fn+1 >= lim) goto fail;else *fn++ = *d++; + if (!DomainEndsInDot(domain)) { if (fn+1 >= lim) goto fail;else *fn++ = '.';} + + *fn = '\0'; + return kDNSServiceErr_NoError; + +fail: + *fn = '\0'; + return kDNSServiceErr_BadParam; +} /********************************************************************************************* - * - * TXT Record Construction Functions - * - *********************************************************************************************/ +* +* 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; +{ + 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]; - }; +struct CompileTimeAssertionCheck_dnssd_clientlib +{ + char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1]; +}; void DNSSD_API TXTRecordCreate - ( - TXTRecordRef *txtRecord, - uint16_t bufferLen, - void *buffer - ) - { - txtRec->buffer = buffer; - txtRec->buflen = buffer ? bufferLen : (uint16_t)0; - txtRec->datalen = 0; - txtRec->malloced = 0; - } +( + 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); - } +{ + 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); - } +( + 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); - } +( + 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 - * - *********************************************************************************************/ +* +* 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); - } +( + 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 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); - } +( + 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); - } +( + uint16_t txtLen, + const void *txtRecord, + uint16_t itemIndex, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value +) +{ + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (p<e && count<itemIndex) { p += 1 + p[0]; count++; } // Find requested item + if (p<e && p + 1 + p[0] <= e) // If valid + { + uint8_t *x = p+1; + unsigned long len = 0; + e = p + 1 + p[0]; + while (x+len<e && x[len] != '=') len++; + if (len >= keyBufLen) return(kDNSServiceErr_NoMemory); + memcpy(key, x, len); + key[len] = 0; + if (x+len<e) // If we found '=' + { + *value = x + len + 1; + *valueLen = (uint8_t)(p[0] - (len + 1)); + } + else + { + *value = NULL; + *valueLen = 0; + } + return(kDNSServiceErr_NoError); + } + return(kDNSServiceErr_Invalid); +} + +/********************************************************************************************* +* +* SCCS-compatible version string +* +*********************************************************************************************/ + +// For convenience when using the "strings" command, this is the last thing in the file + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +// NOT static -- otherwise the compiler may optimize it out +// The "@(#) " pattern is a special prefix the "what" command looks for +const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; diff --git a/usr/src/lib/libdns_sd/common/dnssd_clientstub.c b/usr/src/lib/libdns_sd/common/dnssd_clientstub.c index 2a22a0e22f..653dad734c 100644 --- a/usr/src/lib/libdns_sd/common/dnssd_clientstub.c +++ b/usr/src/lib/libdns_sd/common/dnssd_clientstub.c @@ -2,1248 +2,2371 @@ * * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. * - * Redistribution and use in source and binary forms, with or without + * 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. + * 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 + * 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 <fcntl.h> + +#if APPLE_OSX_mDNSResponder +#include <mach-o/dyld.h> +#include <uuid/uuid.h> +#include <TargetConditionals.h> +#endif #include "dnssd_ipc.h" #if defined(_WIN32) -#include <winsock2.h> -#include <windows.h> + #define _SSIZE_T + #include <CommonServices.h> + #include <DebugServices.h> + #include <winsock2.h> + #include <ws2tcpip.h> + #include <windows.h> + #include <stdarg.h> + #include <stdio.h> -#define sockaddr_mdns sockaddr_in -#define AF_MDNS AF_INET + #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: "'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) +// Disable warning: "nonstandard extension, function/data pointer conversion in expression" + #pragma warning(disable:4152) extern BOOL IsSystemServiceDisabled(); -#define sleep(X) Sleep((X) * 1000) + #define sleep(X) Sleep((X) * 1000) static int g_initWinsock = 0; - + #define LOG_WARNING kDebugLevelWarning + #define LOG_INFO kDebugLevelInfo +static void syslog( int priority, const char * message, ...) +{ + va_list args; + int len; + char * buffer; + DWORD err = WSAGetLastError(); + (void) priority; + va_start( args, message ); + len = _vscprintf( message, args ) + 1; + buffer = malloc( len * sizeof(char) ); + if ( buffer ) { vsprintf( buffer, message, args ); OutputDebugString( buffer ); free( buffer ); } + WSASetLastError( err ); +} #else -#include <sys/time.h> -#include <sys/socket.h> -#include <syslog.h> + #include <sys/fcntl.h> // For O_RDWR etc. + #include <sys/time.h> + #include <sys/socket.h> + #include <syslog.h> -#define sockaddr_mdns sockaddr_un -#define AF_MDNS AF_LOCAL + #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. +// <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 +// Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp) +//#define USE_NAMED_ERROR_RETURN_SOCKET 1 -// 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; +// If the UDS client has not received a response from the daemon in 60 secs, it is unlikely to get one +// Note: Timeout of 3 secs should be sufficient in normal scenarios, but 60 secs is chosen as a safeguard since +// some clients may come up before mDNSResponder itself after a BOOT and on rare ocassions IOPM/Keychain/D2D calls +// in mDNSResponder's INIT may take a much longer time to return +#define DNSSD_CLIENT_TIMEOUT 60 + +#ifndef CTL_PATH_PREFIX +#define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket." +#endif + +typedef struct +{ + ipc_msg_hdr ipc_hdr; + DNSServiceFlags cb_flags; + uint32_t cb_interface; + DNSServiceErrorType cb_err; +} CallbackHeader; + +typedef struct _DNSServiceRef_t DNSServiceOp; +typedef struct _DNSRecordRef_t DNSRecord; + +#if !defined(_WIN32) +typedef struct +{ + void *AppCallback; // Client callback function and context + void *AppContext; +} SleepKAContext; +#endif + +// client stub callback to process message from server and deliver results to client application +typedef void (*ProcessReplyFn)(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *msg, const char *const end); + +#define ValidatorBits 0x12345678 +#define DNSServiceRefValid(X) (dnssd_SocketValid((X)->sockfd) && (((X)->sockfd ^ (X)->validator) == ValidatorBits)) + +// When using kDNSServiceFlagsShareConnection, there is one primary _DNSServiceOp_t, and zero or more subordinates +// For the primary, the 'next' field points to the first subordinate, and its 'next' field points to the next, and so on. +// For the primary, the 'primary' field is NULL; for subordinates the 'primary' field points back to the associated primary +// +// _DNS_SD_LIBDISPATCH is defined where libdispatch/GCD is available. This does not mean that the application will use the +// DNSServiceSetDispatchQueue API. Hence any new code guarded with _DNS_SD_LIBDISPATCH should still be backwards compatible. +struct _DNSServiceRef_t +{ + DNSServiceOp *next; // For shared connection + DNSServiceOp *primary; // For shared connection + dnssd_sock_t sockfd; // Connected socket between client and daemon + dnssd_sock_t validator; // Used to detect memory corruption, double disposals, etc. + client_context_t uid; // For shared connection requests, each subordinate DNSServiceRef has its own ID, + // unique within the scope of the same shared parent DNSServiceRef + uint32_t op; // request_op_t or reply_op_t + uint32_t max_index; // Largest assigned record index - 0 if no additional records registered + uint32_t logcounter; // Counter used to control number of syslog messages we write + int *moreptr; // Set while DNSServiceProcessResult working on this particular DNSServiceRef + ProcessReplyFn ProcessReply; // Function pointer to the code to handle received messages + void *AppCallback; // Client callback function and context + void *AppContext; + DNSRecord *rec; +#if _DNS_SD_LIBDISPATCH + dispatch_source_t disp_source; + dispatch_queue_t disp_queue; +#endif + void *kacontext; +}; + +struct _DNSRecordRef_t +{ + DNSRecord *recnext; + void *AppContext; + DNSServiceRegisterRecordReply AppCallback; DNSRecordRef recref; uint32_t record_index; // index is unique to the ServiceDiscoveryRef - DNSServiceRef sdr; - } _DNSRecordRef_t; - -// exported functions + client_context_t uid; // For demultiplexing multiple DNSServiceRegisterRecord calls + DNSServiceOp *sdr; +}; -// write len bytes. return 0 on success, -1 on error -static int write_all(dnssd_sock_t sd, char *buf, int len) - { +// Write len bytes. Return 0 on success, -1 on error +static int write_all(dnssd_sock_t sd, char *buf, size_t len) +{ // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. - //if (send(sd, buf, len, MSG_WAITALL) != len) return -1; + //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; + { + ssize_t num_written = send(sd, buf, (long)len, 0); + if (num_written < 0 || (size_t)num_written > len) + { + // Should never happen. If it does, it indicates some OS bug, + // or that the mDNSResponder daemon crashed (which should never happen). + #if !defined(__ppc__) && defined(SO_ISDEFUNCT) + int defunct; + socklen_t dlen = sizeof (defunct); + if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0) + syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + if (!defunct) + syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, + (long)num_written, (long)len, + (num_written < 0) ? dnssd_errno : 0, + (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); + else + syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd); + #else + syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, + (long)num_written, (long)len, + (num_written < 0) ? dnssd_errno : 0, + (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); + #endif + return -1; + } + buf += num_written; + len -= num_written; } + return 0; +} + +enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2 }; -// read len bytes. return 0 on success, -1 on error +// Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for static int read_all(dnssd_sock_t sd, char *buf, int len) - { +{ // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. - //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1; + //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; + { + ssize_t num_read = recv(sd, buf, len, 0); + // It is valid to get an interrupted system call error e.g., somebody attaching + // in a debugger, retry without failing + if ((num_read < 0) && (errno == EINTR)) + { + syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue"); + continue; + } + if ((num_read == 0) || (num_read < 0) || (num_read > len)) + { + int printWarn = 0; + int defunct = 0; + // Should never happen. If it does, it indicates some OS bug, + // or that the mDNSResponder daemon crashed (which should never happen). +#if defined(WIN32) + // <rdar://problem/7481776> Suppress logs for "A non-blocking socket operation + // could not be completed immediately" + if (WSAGetLastError() != WSAEWOULDBLOCK) + printWarn = 1; +#endif +#if !defined(__ppc__) && defined(SO_ISDEFUNCT) + { + socklen_t dlen = sizeof (defunct); + if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0) + syslog(LOG_WARNING, "dnssd_clientstub read_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + if (!defunct) + printWarn = 1; +#endif + if (printWarn) + syslog(LOG_WARNING, "dnssd_clientstub read_all(%d) failed %ld/%ld %d %s", sd, + (long)num_read, (long)len, + (num_read < 0) ? dnssd_errno : 0, + (num_read < 0) ? dnssd_strerror(dnssd_errno) : ""); + else if (defunct) + syslog(LOG_INFO, "dnssd_clientstub read_all(%d) DEFUNCT", sd); + return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : read_all_fail; + } + buf += num_read; + len -= num_read; + } + return read_all_success; +} + +// Returns 1 if more bytes remain to be read on socket descriptor sd, 0 otherwise +static int more_bytes(dnssd_sock_t sd) +{ + struct timeval tv = { 0, 0 }; + fd_set readfds; + fd_set *fs; + int ret; + + if (sd < FD_SETSIZE) + { + fs = &readfds; + FD_ZERO(fs); + } + else + { + // Compute the number of integers needed for storing "sd". Internally fd_set is stored + // as an array of ints with one bit for each fd and hence we need to compute + // the number of ints needed rather than the number of bytes. If "sd" is 32, we need + // two ints and not just one. + int nfdbits = sizeof (int) * 8; + int nints = (sd/nfdbits) + 1; + fs = (fd_set *)calloc(nints, sizeof(int)); + if (fs == NULL) + { + syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); + return 0; + } + } + FD_SET(sd, fs); + ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (fs != &readfds) + free(fs); + return (ret > 0); +} + +// set_waitlimit() implements a timeout using select. It is called from deliver_request() before recv() OR accept() +// to ensure the UDS clients are not blocked in these system calls indefinitely. +// Note: Ideally one should never be blocked here, because it indicates either mDNSResponder daemon is not yet up/hung/ +// superbusy/crashed or some other OS bug. For eg: On Windows which suffers from 3rd party software +// (primarily 3rd party firewall software) interfering with proper functioning of the TCP protocol stack it is possible +// the next operation on this socket(recv/accept) is blocked since we depend on TCP to communicate with the system service. +static int set_waitlimit(dnssd_sock_t sock, int timeout) +{ + int gDaemonErr = kDNSServiceErr_NoError; + + // To prevent stack corruption since select does not work with timeout if fds > FD_SETSIZE(1024) + if (!gDaemonErr && sock < FD_SETSIZE) + { + struct timeval tv; + fd_set set; + + FD_ZERO(&set); + FD_SET(sock, &set); + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (!select((int)(sock + 1), &set, NULL, NULL, &tv)) + { + // Ideally one should never hit this case: See comments before set_waitlimit() + syslog(LOG_WARNING, "dnssd_clientstub set_waitlimit:_daemon timed out (%d secs) without any response: Socket %d", timeout, sock); + gDaemonErr = kDNSServiceErr_Timeout; + } } + return gDaemonErr; +} /* create_hdr * - * allocate and initialize an ipc message header. value of len should initially be the - * length of the data, and is set to the value of the data plus the header. data_start - * is set to point to the beginning of the data section. reuse_socket should be non-zero - * for calls that can receive an immediate error return value on their primary socket. + * allocate and initialize an ipc message header. Value of len should initially be the + * length of the data, and is set to the value of the data plus the header. data_start + * is set to point to the beginning of the data section. SeparateReturnSocket should be + * non-zero for calls that can't receive an immediate error return value on their primary + * socket, and therefore require a separate return path for the error code result. * if zero, the path to a control socket is appended at the beginning of the message buffer. * data_start is set past this string. */ - -static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int reuse_socket) - { +static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int SeparateReturnSocket, DNSServiceOp *ref) +{ char *msg = NULL; ipc_msg_hdr *hdr; int datalen; #if !defined(USE_TCP_LOOPBACK) - char ctrl_path[256]; + char ctrl_path[64] = ""; // "/var/tmp/dnssd_result_socket.xxxxxxxxxx-xxx-xxxxxx" #endif - if (!reuse_socket) - { + if (SeparateReturnSocket) + { #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 += 2; // Allocate space for two-byte port number +#elif defined(USE_NAMED_ERROR_RETURN_SOCKET) + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) + { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: gettimeofday failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); return NULL; } + sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), + (unsigned long)(tv.tv_sec & 0xFFF), (unsigned long)(tv.tv_usec)); *len += strlen(ctrl_path) + 1; +#else + *len += 1; // Allocate space for single zero byte (empty C string) #endif - } + } datalen = (int) *len; *len += sizeof(ipc_msg_hdr); - // write message to buffer + // 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; + if (!msg) { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: malloc failed"); return NULL; } + + memset(msg, 0, *len); + hdr = (ipc_msg_hdr *)msg; + hdr->version = VERSION; + hdr->datalen = datalen; + hdr->ipc_flags = 0; + hdr->op = op; + hdr->client_context = ref->uid; + hdr->reg_index = 0; *data_start = msg + sizeof(ipc_msg_hdr); #if defined(USE_TCP_LOOPBACK) - // Put dummy data in for the port, since we don't know what - // it is yet. The data will get filled in before we - // send the message. This happens in deliver_request(). - if (!reuse_socket) put_short(0, data_start); + // Put dummy data in for the port, since we don't know what it is yet. + // The data will get filled in before we send the message. This happens in deliver_request(). + if (SeparateReturnSocket) put_uint16(0, data_start); #else - if (!reuse_socket) put_string(ctrl_path, data_start); + if (SeparateReturnSocket) put_string(ctrl_path, data_start); #endif return hdr; - } +} - // return a connected service ref (deallocate with DNSServiceRefDeallocate) -static DNSServiceRef connect_to_server(void) +static void FreeDNSRecords(DNSServiceOp *sdRef) +{ + DNSRecord *rec = sdRef->rec; + while (rec) { - dnssd_sockaddr_t saddr; - DNSServiceRef sdr; - int NumTries = 0; + DNSRecord *next = rec->recnext; + free(rec); + rec = next; + } +} -#if defined(_WIN32) - if (!g_initWinsock) - { - WSADATA wsaData; - DNSServiceErrorType err; - - g_initWinsock = 1; +static void FreeDNSServiceOp(DNSServiceOp *x) +{ + // We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed + // then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket) + if ((x->sockfd ^ x->validator) != ValidatorBits) + syslog(LOG_WARNING, "dnssd_clientstub attempt to dispose invalid DNSServiceRef %p %08X %08X", x, x->sockfd, x->validator); + else + { + x->next = NULL; + x->primary = NULL; + x->sockfd = dnssd_InvalidSocket; + x->validator = 0xDDDDDDDD; + x->op = request_op_none; + x->max_index = 0; + x->logcounter = 0; + x->moreptr = NULL; + x->ProcessReply = NULL; + x->AppCallback = NULL; + x->AppContext = NULL; +#if _DNS_SD_LIBDISPATCH + if (x->disp_source) dispatch_release(x->disp_source); + x->disp_source = NULL; + x->disp_queue = NULL; +#endif + // DNSRecords may have been added to subordinate sdRef e.g., DNSServiceRegister/DNSServiceAddRecord + // or on the main sdRef e.g., DNSServiceCreateConnection/DNSServiveRegisterRecord. DNSRecords may have + // been freed if the application called DNSRemoveRecord + FreeDNSRecords(x); + if (x->kacontext) + { + free(x->kacontext); + x->kacontext = NULL; + } + free(x); + } +} - err = WSAStartup( MAKEWORD( 2, 2 ), &wsaData ); +// Return a connected service ref (deallocate with DNSServiceRefDeallocate) +static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext) +{ + int NumTries = 0; - if (err != 0) return NULL; - } + dnssd_sockaddr_t saddr; + DNSServiceOp *sdr; - // <rdar://problem/4096913> If the system service is disabled, we only want to try - // to connect once + if (!ref) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef"); + return kDNSServiceErr_BadParam; + } - if ( IsSystemServiceDisabled() ) - { - NumTries = DNSSD_CLIENT_MAXTRIES; - } + if (flags & kDNSServiceFlagsShareConnection) + { + if (!*ref) + { + syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef"); + return kDNSServiceErr_BadParam; + } + if (!DNSServiceRefValid(*ref) || ((*ref)->op != connection_request && (*ref)->op != connection_delegate_request) || (*ref)->primary) + { + syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X op %d", + (*ref), (*ref)->sockfd, (*ref)->validator, (*ref)->op); + *ref = NULL; + return kDNSServiceErr_BadReference; + } + } + #if defined(_WIN32) + if (!g_initWinsock) + { + WSADATA wsaData; + g_initWinsock = 1; + if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; } + } + // <rdar://problem/4096913> If the system service is disabled, we only want to try to connect once + if (IsSystemServiceDisabled()) + NumTries = DNSSD_CLIENT_MAXTRIES; + #endif + + sdr = malloc(sizeof(DNSServiceOp)); + if (!sdr) + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed"); + *ref = NULL; + return kDNSServiceErr_NoMemory; + } + sdr->next = NULL; + sdr->primary = NULL; + sdr->sockfd = dnssd_InvalidSocket; + sdr->validator = sdr->sockfd ^ ValidatorBits; + sdr->op = op; + sdr->max_index = 0; + sdr->logcounter = 0; + sdr->moreptr = NULL; + sdr->uid.u32[0] = 0; + sdr->uid.u32[1] = 0; + sdr->ProcessReply = ProcessReply; + sdr->AppCallback = AppCallback; + sdr->AppContext = AppContext; + sdr->rec = NULL; +#if _DNS_SD_LIBDISPATCH + sdr->disp_source = NULL; + sdr->disp_queue = NULL; #endif + sdr->kacontext = NULL; - 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) + if (flags & kDNSServiceFlagsShareConnection) + { + DNSServiceOp **p = &(*ref)->next; // Append ourselves to end of primary's list + while (*p) + p = &(*p)->next; + *p = sdr; + // Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear + if (++(*ref)->uid.u32[0] == 0) + ++(*ref)->uid.u32[1]; // In parent DNSServiceOp increment UID counter + sdr->primary = *ref; // Set our primary pointer + sdr->sockfd = (*ref)->sockfd; // Inherit primary's socket + sdr->validator = (*ref)->validator; + sdr->uid = (*ref)->uid; + //printf("ConnectToServer sharing socket %d\n", sdr->sockfd); + } + else { - 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); + #ifdef SO_NOSIGPIPE + const unsigned long optval = 1; + #endif + #ifndef USE_TCP_LOOPBACK + char* uds_serverpath = getenv(MDNS_UDS_SERVERPATH_ENVVAR); + if (uds_serverpath == NULL) + uds_serverpath = MDNS_UDS_SERVERPATH; + #endif + *ref = NULL; + sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0); + sdr->validator = sdr->sockfd ^ ValidatorBits; + if (!dnssd_SocketValid(sdr->sockfd)) + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: socket failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + FreeDNSServiceOp(sdr); + return kDNSServiceErr_NoMemory; + } + #ifdef SO_NOSIGPIPE + // Some environments (e.g. OS X) support turning off SIGPIPE for a socket + if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_NOSIGPIPE failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + #endif + #if defined(USE_TCP_LOOPBACK) + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + saddr.sin_port = htons(MDNS_TCP_SERVERPORT); + #else + saddr.sun_family = AF_LOCAL; + strcpy(saddr.sun_path, uds_serverpath); + #if !defined(__ppc__) && defined(SO_DEFUNCTOK) + { + int defunct = 1; + if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + #endif + #endif + + while (1) + { + int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); + if (!err) + break; // If we succeeded, return sdr + // If we failed, then it may be because the daemon is still launching. + // This can happen for processes that launch early in the boot process, while the + // daemon is still coming up. Rather than fail here, we wait 1 sec and try again. + // If, after DNSSD_CLIENT_MAXTRIES, we still can't connect to the daemon, + // then we give up and return a failure code. + if (++NumTries < DNSSD_CLIENT_MAXTRIES) + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect()-> No of tries: %d", NumTries); + sleep(1); // Sleep a bit, then try again + } + else + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed path:%s Socket:%d Err:%d Errno:%d %s", + uds_serverpath, sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno)); + dnssd_close(sdr->sockfd); + FreeDNSServiceOp(sdr); + return kDNSServiceErr_ServiceNotRunning; + } + } + //printf("ConnectToServer opened socket %d\n", sdr->sockfd); + } + + *ref = sdr; + return kDNSServiceErr_NoError; +} + +#define deliver_request_bailout(MSG) \ + do { syslog(LOG_WARNING, "dnssd_clientstub deliver_request: %s failed %d (%s)", (MSG), dnssd_errno, dnssd_strerror(dnssd_errno)); goto cleanup; } while(0) + +static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) +{ + uint32_t datalen = hdr->datalen; // We take a copy here because we're going to convert hdr->datalen to network byte order + #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET) + char *const data = (char *)hdr + sizeof(ipc_msg_hdr); + #endif dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket; - int ret; - dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr); - DNSServiceErrorType err = kDNSServiceErr_Unknown; + DNSServiceErrorType err = kDNSServiceErr_Unknown; // Default for the "goto cleanup" cases + int MakeSeparateReturnSocket = 0; + + // Note: need to check hdr->op, not sdr->op. + // hdr->op contains the code for the specific operation we're currently doing, whereas sdr->op + // contains the original parent DNSServiceOp (e.g. for an add_record_request, hdr->op will be + // add_record_request but the parent sdr->op will be connection_request or reg_service_request) + if (sdr->primary || + hdr->op == reg_record_request || hdr->op == add_record_request || hdr->op == update_record_request || hdr->op == remove_record_request) + MakeSeparateReturnSocket = 1; + + if (!DNSServiceRefValid(sdr)) + { + if (hdr) + free(hdr); + syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator); + return kDNSServiceErr_BadReference; + } - if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown; + if (!hdr) + { + syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); + 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 (MakeSeparateReturnSocket) + { + #if defined(USE_TCP_LOOPBACK) + { + union { uint16_t s; u_char b[2]; } port; + dnssd_sockaddr_t caddr; + dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr); + listenfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (!dnssd_SocketValid(listenfd)) deliver_request_bailout("TCP socket"); + + caddr.sin_family = AF_INET; + caddr.sin_port = 0; + caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + if (bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)) < 0) deliver_request_bailout("TCP bind"); + if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) deliver_request_bailout("TCP getsockname"); + if (listen(listenfd, 1) < 0) deliver_request_bailout("TCP listen"); + port.s = caddr.sin_port; + data[0] = port.b[0]; // don't switch the byte order, as the + data[1] = port.b[1]; // daemon expects it in network byte order + } + #elif defined(USE_NAMED_ERROR_RETURN_SOCKET) + { + mode_t mask; + int bindresult; + dnssd_sockaddr_t caddr; + listenfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (!dnssd_SocketValid(listenfd)) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET socket"); + + caddr.sun_family = AF_LOCAL; + // According to Stevens (section 3.2), there is no portable way to + // determine whether sa_len is defined on a particular platform. + #ifndef NOT_HAVE_SA_LEN + caddr.sun_len = sizeof(struct sockaddr_un); + #endif + strcpy(caddr.sun_path, data); + mask = umask(0); + bindresult = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)); + umask(mask); + if (bindresult < 0) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET bind"); + if (listen(listenfd, 1) < 0) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET listen"); + } + #else + { + dnssd_sock_t sp[2]; + if (socketpair(AF_DNSSD, SOCK_STREAM, 0, sp) < 0) deliver_request_bailout("socketpair"); + else + { + errsd = sp[0]; // We'll read our four-byte error code from sp[0] + listenfd = sp[1]; // We'll send sp[1] to the daemon + #if !defined(__ppc__) && defined(SO_DEFUNCTOK) + { + int defunct = 1; + if (setsockopt(errsd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + #endif + } + } + #endif + } -#if defined(USE_TCP_LOOPBACK) - { - 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 - } +#if !defined(USE_TCP_LOOPBACK) && !defined(USE_NAMED_ERROR_RETURN_SOCKET) + // If we're going to make a separate error return socket, and pass it to the daemon + // using sendmsg, then we'll hold back one data byte to go with it. + // On some versions of Unix (including Leopard) sending a control message without + // any associated data does not work reliably -- e.g. one particular issue we ran + // into is that if the receiving program is in a kqueue loop waiting to be notified + // of the received message, it doesn't get woken up when the control message arrives. + if (MakeSeparateReturnSocket || sdr->op == send_bpf) + datalen--; // Okay to use sdr->op when checking for op == send_bpf +#endif + + // At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to + ConvertHeaderBytes(hdr); + //syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %lu bytes", (unsigned long)(datalen + sizeof(ipc_msg_hdr))); + //if (MakeSeparateReturnSocket) syslog(LOG_WARNING, "dnssd_clientstub deliver_request name is %s", data); +#if TEST_SENDING_ONE_BYTE_AT_A_TIME + unsigned int i; + for (i=0; i<datalen + sizeof(ipc_msg_hdr); i++) + { + syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %d", i); + if (write_all(sdr->sockfd, ((char *)hdr)+i, 1) < 0) + { syslog(LOG_WARNING, "write_all (byte %u) failed", i); goto cleanup; } + usleep(10000); + } #else - { - 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); + if (write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)) < 0) + { + // write_all already prints an error message if there is an error writing to + // the socket except for DEFUNCT. Logging here is unnecessary and also wrong + // in the case of DEFUNCT sockets + syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed", + sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr))); + goto cleanup; + } #endif - //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); - } + + if (!MakeSeparateReturnSocket) + errsd = sdr->sockfd; + if (MakeSeparateReturnSocket || sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf + { +#if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET) + // At this point we may wait in accept for a few milliseconds waiting for the daemon to connect back to us, + // but that's okay -- the daemon should not take more than a few milliseconds to respond. + // set_waitlimit() ensures we do not block indefinitely just in case something is wrong + dnssd_sockaddr_t daddr; + dnssd_socklen_t len = sizeof(daddr); + if ((err = set_waitlimit(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) + goto cleanup; + errsd = accept(listenfd, (struct sockaddr *)&daddr, &len); + if (!dnssd_SocketValid(errsd)) + deliver_request_bailout("accept"); +#else + + struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS + struct msghdr msg; + struct cmsghdr *cmsg; + char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))]; + + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + if (MakeSeparateReturnSocket || sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf + { + if (sdr->op == send_bpf) + { + int i; + char p[12]; // Room for "/dev/bpf999" with terminating null + for (i=0; i<100; i++) + { + snprintf(p, sizeof(p), "/dev/bpf%d", i); + listenfd = open(p, O_RDWR, 0); + //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p); + if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY) + syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno)); + if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break; + } + } + msg.msg_control = cbuf; + msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(dnssd_sock_t)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd; + } + +#if TEST_KQUEUE_CONTROL_MESSAGE_BUG + sleep(1); #endif - } - 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 DEBUG_64BIT_SCM_RIGHTS + syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d %ld %ld %ld/%ld/%ld/%ld", + errsd, listenfd, sizeof(dnssd_sock_t), sizeof(void*), + sizeof(struct cmsghdr) + sizeof(dnssd_sock_t), + CMSG_LEN(sizeof(dnssd_sock_t)), (long)CMSG_SPACE(sizeof(dnssd_sock_t)), + (long)((char*)CMSG_DATA(cmsg) + 4 - cbuf)); +#endif // DEBUG_64BIT_SCM_RIGHTS - if (reuse_sd) errsd = sdr->sockfd; - else + if (sendmsg(sdr->sockfd, &msg, 0) < 0) { - //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; + syslog(LOG_WARNING, "dnssd_clientstub deliver_request ERROR: sendmsg failed read sd=%d write sd=%d errno %d (%s)", + errsd, listenfd, dnssd_errno, dnssd_strerror(dnssd_errno)); + err = kDNSServiceErr_Incompatible; + goto cleanup; } - if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0) - err = kDNSServiceErr_Unknown; - else - err = ntohl(err); +#if DEBUG_64BIT_SCM_RIGHTS + syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d okay", errsd, listenfd); +#endif // DEBUG_64BIT_SCM_RIGHTS - //syslog(LOG_WARNING, "deliver_request: retrieved error code %d\n", err); +#endif + // Close our end of the socketpair *before* calling read_all() to get the four-byte error code. + // Otherwise, if the daemon closes our socket (or crashes), we will have to wait for a timeout + // in read_all() because the socket is not closed (we still have an open reference to it) + // Note: listenfd is overwritten in the case of send_bpf above and that will be closed here + // for send_bpf operation. + dnssd_close(listenfd); + listenfd = dnssd_InvalidSocket; // Make sure we don't close it a second time in the cleanup handling below + } + + // At this point we may wait in read_all for a few milliseconds waiting for the daemon to send us the error code, + // but that's okay -- the daemon should not take more than a few milliseconds to respond. + // set_waitlimit() ensures we do not block indefinitely just in case something is wrong + if (sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf + err = kDNSServiceErr_NoError; + else if ((err = set_waitlimit(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError) + { + if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0) + err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us + else + err = ntohl(err); + } + //syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err); cleanup: - if (!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); + if (MakeSeparateReturnSocket) + { + if (dnssd_SocketValid(listenfd)) dnssd_close(listenfd); + if (dnssd_SocketValid(errsd)) dnssd_close(errsd); +#if defined(USE_NAMED_ERROR_RETURN_SOCKET) + // syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removing UDS: %s", data); + if (unlink(data) != 0) + syslog(LOG_WARNING, "dnssd_clientstub WARNING: unlink(\"%s\") failed errno %d (%s)", data, dnssd_errno, dnssd_strerror(dnssd_errno)); + // else syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removed UDS: %s", data); #endif - } - if (msg) free(msg); - return err; } + free(hdr); + return err; +} + int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) +{ + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with NULL DNSServiceRef"); return dnssd_InvalidSocket; } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with invalid DNSServiceRef %p %08X %08X", + sdRef, sdRef->sockfd, sdRef->validator); + return dnssd_InvalidSocket; + } + + if (sdRef->primary) { - if (!sdRef) return -1; + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef); + return dnssd_InvalidSocket; + } + return (int) sdRef->sockfd; +} + +#if _DNS_SD_LIBDISPATCH +static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error) +{ + DNSServiceOp *sdr = sdRef; + DNSServiceOp *sdrNext; + DNSRecord *rec; + DNSRecord *recnext; + int morebytes; + + while (sdr) + { + // We can't touch the sdr after the callback as it can be deallocated in the callback + sdrNext = sdr->next; + morebytes = 1; + sdr->moreptr = &morebytes; + switch (sdr->op) + { + case resolve_request: + if (sdr->AppCallback) ((DNSServiceResolveReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, sdr->AppContext); + break; + case query_request: + if (sdr->AppCallback) ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, 0, sdr->AppContext); + break; + case addrinfo_request: + if (sdr->AppCallback) ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, NULL, 0, sdr->AppContext); + break; + case browse_request: + if (sdr->AppCallback) ((DNSServiceBrowseReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, NULL, sdr->AppContext); + break; + case reg_service_request: + if (sdr->AppCallback) ((DNSServiceRegisterReply) sdr->AppCallback)(sdr, 0, error, NULL, 0, NULL, sdr->AppContext); + break; + case enumeration_request: + if (sdr->AppCallback) ((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, sdr->AppContext); + break; + case connection_request: + case connection_delegate_request: + // This means Register Record, walk the list of DNSRecords to do the callback + rec = sdr->rec; + while (rec) + { + recnext = rec->recnext; + if (rec->AppCallback) ((DNSServiceRegisterRecordReply)rec->AppCallback)(sdr, 0, 0, error, rec->AppContext); + // The Callback can call DNSServiceRefDeallocate which in turn frees sdr and all the records. + // Detect that and return early + if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:Record: CallbackwithError morebytes zero"); return;} + rec = recnext; + } + break; + case port_mapping_request: + if (sdr->AppCallback) ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, 0, 0, error, 0, 0, 0, 0, 0, sdr->AppContext); + break; + default: + syslog(LOG_WARNING, "dnssd_clientstub CallbackWithError called with bad op %d", sdr->op); + } + // If DNSServiceRefDeallocate was called in the callback, morebytes will be zero. As the sdRef + // (and its subordinates) have been freed, we should not proceed further. Note that when we + // call the callback with a subordinate sdRef the application can call DNSServiceRefDeallocate + // on the main sdRef and DNSServiceRefDeallocate handles this case by walking all the sdRefs and + // clears the moreptr so that we can terminate here. + // + // If DNSServiceRefDeallocate was not called in the callback, then set moreptr to NULL so that + // we don't access the stack variable after we return from this function. + if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:sdRef: CallbackwithError morebytes zero sdr %p", sdr); return;} + else {sdr->moreptr = NULL;} + sdr = sdrNext; } +} +#endif // _DNS_SD_LIBDISPATCH -// handle reply from server, calling application client callback. If there is no reply +// Handle reply from server, calling application client callback. If there is no reply // from the daemon on the socket contained in sdRef, the call will block. DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) +{ + int morebytes = 0; + + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + + if (!DNSServiceRefValid(sdRef)) { - ipc_msg_hdr hdr; - char *data; - int rderr; + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return kDNSServiceErr_BadReference; + } - if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply) + if (sdRef->primary) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef); 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; + if (!sdRef->ProcessReply) + { + static int num_logs = 0; + if (num_logs < 10) syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function"); + if (num_logs < 1000) num_logs++;else sleep(1); + return kDNSServiceErr_BadReference; } + do + { + CallbackHeader cbh; + char *data; + + // return NoError on EWOULDBLOCK. This will handle the case + // where a non-blocking socket is told there is data, but it was a false positive. + // On error, read_all will write a message to syslog for us, so don't need to duplicate that here + // Note: If we want to properly support using non-blocking sockets in the future + int result = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr)); + if (result == read_all_fail) + { + // Set the ProcessReply to NULL before callback as the sdRef can get deallocated + // in the callback. + sdRef->ProcessReply = NULL; +#if _DNS_SD_LIBDISPATCH + // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult + // is not called by the application and hence need to communicate the error. Cancel the + // source so that we don't get any more events + // Note: read_all fails if we could not read from the daemon which can happen if the + // daemon dies or the file descriptor is disconnected (defunct). + if (sdRef->disp_source) + { + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning); + } +#endif + // Don't touch sdRef anymore as it might have been deallocated + return kDNSServiceErr_ServiceNotRunning; + } + else if (result == read_all_wouldblock) + { + if (morebytes && sdRef->logcounter < 100) + { + sdRef->logcounter++; + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult error: select indicated data was waiting but read_all returned EWOULDBLOCK"); + } + return kDNSServiceErr_NoError; + } + + ConvertHeaderBytes(&cbh.ipc_hdr); + if (cbh.ipc_hdr.version != VERSION) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult daemon version %d does not match client version %d", cbh.ipc_hdr.version, VERSION); + sdRef->ProcessReply = NULL; + return kDNSServiceErr_Incompatible; + } + + data = malloc(cbh.ipc_hdr.datalen); + if (!data) return kDNSServiceErr_NoMemory; + if (read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen) < 0) // On error, read_all will write a message to syslog for us + { + // Set the ProcessReply to NULL before callback as the sdRef can get deallocated + // in the callback. + sdRef->ProcessReply = NULL; +#if _DNS_SD_LIBDISPATCH + // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult + // is not called by the application and hence need to communicate the error. Cancel the + // source so that we don't get any more events + if (sdRef->disp_source) + { + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning); + } +#endif + // Don't touch sdRef anymore as it might have been deallocated + free(data); + return kDNSServiceErr_ServiceNotRunning; + } + else + { + const char *ptr = data; + cbh.cb_flags = get_flags (&ptr, data + cbh.ipc_hdr.datalen); + cbh.cb_interface = get_uint32 (&ptr, data + cbh.ipc_hdr.datalen); + cbh.cb_err = get_error_code(&ptr, data + cbh.ipc_hdr.datalen); + + // CAUTION: We have to handle the case where the client calls DNSServiceRefDeallocate from within the callback function. + // To do this we set moreptr to point to morebytes. If the client does call DNSServiceRefDeallocate(), + // then that routine will clear morebytes for us, and cause us to exit our loop. + morebytes = more_bytes(sdRef->sockfd); + if (morebytes) + { + cbh.cb_flags |= kDNSServiceFlagsMoreComing; + sdRef->moreptr = &morebytes; + } + if (ptr) sdRef->ProcessReply(sdRef, &cbh, ptr, data + cbh.ipc_hdr.datalen); + // Careful code here: + // If morebytes is non-zero, that means we set sdRef->moreptr above, and the operation was not + // cancelled out from under us, so now we need to clear sdRef->moreptr so we don't leave a stray + // dangling pointer pointing to a long-gone stack variable. + // If morebytes is zero, then one of two thing happened: + // (a) morebytes was 0 above, so we didn't set sdRef->moreptr, so we don't need to clear it + // (b) morebytes was 1 above, and we set sdRef->moreptr, but the operation was cancelled (with DNSServiceRefDeallocate()), + // so we MUST NOT try to dereference our stale sdRef pointer. + if (morebytes) sdRef->moreptr = NULL; + } + free(data); + } while (morebytes); + + return kDNSServiceErr_NoError; +} + void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef) +{ + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with NULL DNSServiceRef"); return; } + + if (!DNSServiceRefValid(sdRef)) // Also verifies dnssd_SocketValid(sdRef->sockfd) for us too { - if (!sdRef) return; - if (sdRef->sockfd > 0) dnssd_close(sdRef->sockfd); - free(sdRef); + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return; } -static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + // If we're in the middle of a DNSServiceProcessResult() invocation for this DNSServiceRef, clear its morebytes flag to break it out of its while loop + if (sdRef->moreptr) *(sdRef->moreptr) = 0; + + if (sdRef->primary) // If this is a subordinate DNSServiceOp, just send a 'stop' command + { + DNSServiceOp **p = &sdRef->primary->next; + while (*p && *p != sdRef) p = &(*p)->next; + if (*p) + { + char *ptr; + size_t len = 0; + ipc_msg_hdr *hdr = create_hdr(cancel_request, &len, &ptr, 0, sdRef); + if (hdr) + { + ConvertHeaderBytes(hdr); + write_all(sdRef->sockfd, (char *)hdr, len); + free(hdr); + } + *p = sdRef->next; + FreeDNSServiceOp(sdRef); + } + } + else // else, make sure to terminate all subordinates as well { - DNSServiceFlags flags; +#if _DNS_SD_LIBDISPATCH + // The cancel handler will close the fd if a dispatch source has been set + if (sdRef->disp_source) + { + // By setting the ProcessReply to NULL, we make sure that we never call + // the application callbacks ever, after returning from this function. We + // assume that DNSServiceRefDeallocate is called from the serial queue + // that was passed to DNSServiceSetDispatchQueue. Hence, dispatch_source_cancel + // should cancel all the blocks on the queue and hence there should be no more + // callbacks when we return from this function. Setting ProcessReply to NULL + // provides extra protection. + sdRef->ProcessReply = NULL; + shutdown(sdRef->sockfd, SHUT_WR); + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + } + // if disp_queue is set, it means it used the DNSServiceSetDispatchQueue API. In that case, + // when the source was cancelled, the fd was closed in the handler. Currently the source + // is cancelled only when the mDNSResponder daemon dies + else if (!sdRef->disp_queue) dnssd_close(sdRef->sockfd); +#else + dnssd_close(sdRef->sockfd); +#endif + // Free DNSRecords added in DNSRegisterRecord if they have not + // been freed in DNSRemoveRecord + while (sdRef) + { + DNSServiceOp *p = sdRef; + sdRef = sdRef->next; + // When there is an error reading from the daemon e.g., bad fd, CallbackWithError + // is called which sets moreptr. It might set the moreptr on a subordinate sdRef + // but the application might call DNSServiceRefDeallocate with the main sdRef from + // the callback. Hence, when we loop through the subordinate sdRefs, we need + // to clear the moreptr so that CallbackWithError can terminate itself instead of + // walking through the freed sdRefs. + if (p->moreptr) *(p->moreptr) = 0; + FreeDNSServiceOp(p); + } + } +} + +DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void *result, uint32_t *size) +{ + char *ptr; + size_t len = strlen(property) + 1; + ipc_msg_hdr *hdr; + DNSServiceOp *tmp; + uint32_t actualsize; + + DNSServiceErrorType err = ConnectToServer(&tmp, 0, getproperty_request, NULL, NULL, NULL); + if (err) return err; + + hdr = create_hdr(getproperty_request, &len, &ptr, 0, tmp); + if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; } + + put_string(property, &ptr); + err = deliver_request(hdr, tmp); // Will free hdr for us + if (read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize)) < 0) + { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } + + actualsize = ntohl(actualsize); + if (read_all(tmp->sockfd, (char*)result, actualsize < *size ? actualsize : *size) < 0) + { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } + DNSServiceRefDeallocate(tmp); + + // Swap version result back to local process byte order + if (!strcmp(property, kDNSServiceProperty_DaemonVersion) && *size >= 4) + *(uint32_t*)result = ntohl(*(uint32_t*)result); + + *size = actualsize; + return kDNSServiceErr_NoError; +} + +DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t *pid) +{ + char *ptr; + ipc_msg_hdr *hdr; + DNSServiceOp *tmp; + size_t len = sizeof(int32_t); + + DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL); + if (err) + return err; + + hdr = create_hdr(getpid_request, &len, &ptr, 0, tmp); + if (!hdr) + { + DNSServiceRefDeallocate(tmp); + return kDNSServiceErr_NoMemory; + } + + put_uint16(srcport, &ptr); + err = deliver_request(hdr, tmp); // Will free hdr for us + + if (read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)) < 0) + { + DNSServiceRefDeallocate(tmp); + return kDNSServiceErr_ServiceNotRunning; + } + + DNSServiceRefDeallocate(tmp); + return kDNSServiceErr_NoError; +} + +static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end) +{ char fullname[kDNSServiceMaxDomainName]; char target[kDNSServiceMaxDomainName]; uint16_t txtlen; union { uint16_t s; u_char b[2]; } port; - 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; + + get_string(&data, end, fullname, kDNSServiceMaxDomainName); + get_string(&data, end, target, kDNSServiceMaxDomainName); + if (!data || data + 2 > end) goto fail; + port.b[0] = *data++; port.b[1] = *data++; - txtlen = get_short(&data); - txtrecord = (unsigned char *)get_rdata(&data, txtlen); + txtlen = get_uint16(&data, end); + txtrecord = (unsigned char *)get_rdata(&data, end, 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); - } + if (!data) goto fail; + ((DNSServiceResolveReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, fullname, target, port.s, txtlen, txtrecord, sdr->AppContext); + return; + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +fail: + syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon"); +} + +#if TARGET_OS_EMBEDDED + +static int32_t libSystemVersion = 0; + +// Return true if the iOS application linked against a version of libsystem where P2P +// interfaces were included by default when using kDNSServiceInterfaceIndexAny. +// Using 160.0.0 == 0xa00000 as the version threshold. +static int includeP2PWithIndexAny() +{ + if (libSystemVersion == 0) + libSystemVersion = NSVersionOfLinkTimeLibrary("System"); + + if (libSystemVersion < 0xa00000) + return 1; + else + return 0; +} + +#else // TARGET_OS_EMBEDDED + +// always return false for non iOS platforms +static int includeP2PWithIndexAny() +{ + return 0; +} + +#endif // TARGET_OS_EMBEDDED 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; +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context +) +{ + char *ptr; size_t len; ipc_msg_hdr *hdr; - DNSServiceRef sdr; DNSServiceErrorType err; - if (!sdRef) return kDNSServiceErr_BadParam; - *sdRef = NULL; + if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; - if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; + // Need a real InterfaceID for WakeOnResolve + if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 && + ((interfaceIndex == kDNSServiceInterfaceIndexAny) || + (interfaceIndex == kDNSServiceInterfaceIndexLocalOnly) || + (interfaceIndex == kDNSServiceInterfaceIndexUnicast) || + (interfaceIndex == kDNSServiceInterfaceIndexP2P))) + { + return kDNSServiceErr_BadParam; + } + + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, (void *)callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL - // calculate total message length + // 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; + hdr = create_hdr(resolve_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } put_flags(flags, &ptr); - put_long(interfaceIndex, &ptr); + put_uint32(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; - + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } 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; +static void handle_query_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + uint32_t ttl; char name[kDNSServiceMaxDomainName]; uint16_t rrtype, rrclass, rdlen; - 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; - } + const char *rdata; + + get_string(&data, end, name, kDNSServiceMaxDomainName); + rrtype = get_uint16(&data, end); + rrclass = get_uint16(&data, end); + rdlen = get_uint16(&data, end); + rdata = get_rdata(&data, end, rdlen); + ttl = get_uint32(&data, end); + + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_query_response: error reading result from daemon"); + else ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, name, rrtype, rrclass, rdlen, rdata, ttl, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +} DNSServiceErrorType DNSSD_API DNSServiceQueryRecord - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *name, - uint16_t rrtype, - uint16_t rrclass, - DNSServiceQueryRecordReply callBack, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, void *context - ) - { - char *msg = NULL, *ptr; +) +{ + char *ptr; size_t len; ipc_msg_hdr *hdr; - DNSServiceRef sdr; DNSServiceErrorType err; - if (!sdRef) return kDNSServiceErr_BadParam; - *sdRef = NULL; + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + err = ConnectToServer(sdRef, flags, query_request, handle_query_response, (void *)callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL if (!name) name = "\0"; - // calculate total message length + // Calculate total message length len = sizeof(flags); - len += sizeof(uint32_t); //interfaceIndex + 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; + hdr = create_hdr(query_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } put_flags(flags, &ptr); - put_long(interfaceIndex, &ptr); + put_uint32(interfaceIndex, &ptr); put_string(name, &ptr); - put_short(rrtype, &ptr); - put_short(rrclass, &ptr); + put_uint16(rrtype, &ptr); + put_uint16(rrclass, &ptr); - sdr = connect_to_server(); - if (!sdr) goto error; - err = deliver_request(msg, sdr, 1); - if (err) + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + char hostname[kDNSServiceMaxDomainName]; + uint16_t rrtype, rdlen; + const char *rdata; + uint32_t ttl; + + get_string(&data, end, hostname, kDNSServiceMaxDomainName); + rrtype = get_uint16(&data, end); + (void) get_uint16(&data, end); /* class is not used */ + rdlen = get_uint16(&data, end); + rdata = get_rdata (&data, end, rdlen); + ttl = get_uint32(&data, end); + + // We only generate client callbacks for A and AAAA results (including NXDOMAIN results for + // those types, if the client has requested those with the kDNSServiceFlagsReturnIntermediates). + // Other result types, specifically CNAME referrals, are not communicated to the client, because + // the DNSServiceGetAddrInfoReply interface doesn't have any meaningful way to communiate CNAME referrals. + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_addrinfo_response: error reading result from daemon"); + else if (rrtype == kDNSServiceType_A || rrtype == kDNSServiceType_AAAA) + { + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + const struct sockaddr *const sa = (rrtype == kDNSServiceType_A) ? (struct sockaddr*)&sa4 : (struct sockaddr*)&sa6; + if (rrtype == kDNSServiceType_A) { - DNSServiceRefDeallocate(sdr); - return err; + memset(&sa4, 0, sizeof(sa4)); + #ifndef NOT_HAVE_SA_LEN + sa4.sin_len = sizeof(struct sockaddr_in); + #endif + sa4.sin_family = AF_INET; + // sin_port = 0; + if (!cbh->cb_err) memcpy(&sa4.sin_addr, rdata, rdlen); + } + else + { + memset(&sa6, 0, sizeof(sa6)); + #ifndef NOT_HAVE_SA_LEN + sa6.sin6_len = sizeof(struct sockaddr_in6); + #endif + sa6.sin6_family = AF_INET6; + // sin6_port = 0; + // sin6_flowinfo = 0; + // sin6_scope_id = 0; + if (!cbh->cb_err) + { + memcpy(&sa6.sin6_addr, rdata, rdlen); + if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface; + } } + // Validation results are always delivered separately from the actual results of the + // DNSServiceGetAddrInfo. Set the "addr" to NULL as per the documentation. + // + // Note: If we deliver validation results along with the "addr" in the future, we need + // a way to differentiate the negative response from validation-only response as both + // has zero address. + if (!(cbh->cb_flags & kDNSServiceFlagsValidate)) + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext); + else + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, 0, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function + } +} - sdr->op = query_request; - sdr->process_reply = handle_query_response; - sdr->app_callback = callBack; - sdr->app_context = context; - *sdRef = sdr; - return err; +DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + uint32_t protocol, + const char *hostname, + DNSServiceGetAddrInfoReply callBack, + void *context /* may be NULL */ +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceErrorType err; -error: - if (msg) free(msg); - if (*sdRef) { free(*sdRef); *sdRef = NULL; } - return kDNSServiceErr_Unknown; - } + if (!hostname) return kDNSServiceErr_BadParam; -static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, (void *)callBack, context); + if (err) { - 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); + return err; // On error ConnectToServer leaves *sdRef set to NULL } + // Calculate total message length + len = sizeof(flags); + len += sizeof(uint32_t); // interfaceIndex + len += sizeof(uint32_t); // protocol + len += strlen(hostname) + 1; + + hdr = create_hdr(addrinfo_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_uint32(protocol, &ptr); + put_string(hostname, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +static void handle_browse_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + char replyName[256], replyType[kDNSServiceMaxDomainName], replyDomain[kDNSServiceMaxDomainName]; + get_string(&data, end, replyName, 256); + get_string(&data, end, replyType, kDNSServiceMaxDomainName); + get_string(&data, end, replyDomain, kDNSServiceMaxDomainName); + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_browse_response: error reading result from daemon"); + else ((DNSServiceBrowseReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, replyName, replyType, replyDomain, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +} + DNSServiceErrorType DNSSD_API DNSServiceBrowse - ( - DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *regtype, - const char *domain, - DNSServiceBrowseReply callBack, - void *context - ) - { - char *msg = NULL, *ptr; +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, + DNSServiceBrowseReply callBack, + void *context +) +{ + char *ptr; size_t len; ipc_msg_hdr *hdr; - DNSServiceRef sdr; DNSServiceErrorType err; - if (!sdRef) return kDNSServiceErr_BadParam; - *sdRef = NULL; + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; - if (!domain) domain = ""; + err = ConnectToServer(sdRef, flags, browse_request, handle_browse_response, (void *)callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + if (!domain) domain = ""; len = sizeof(flags); len += sizeof(interfaceIndex); len += strlen(regtype) + 1; len += strlen(domain) + 1; - hdr = create_hdr(browse_request, &len, &ptr, 1); - if (!hdr) goto error; - msg = (char *)hdr; + hdr = create_hdr(browse_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + put_flags(flags, &ptr); - put_long(interfaceIndex, &ptr); + put_uint32(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; + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } 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; +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain); +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain) +{ + DNSServiceOp *tmp; + char *ptr; size_t len = sizeof(flags) + strlen(domain) + 1; - ipc_msg_hdr *hdr = create_hdr(setdomain_request, &len, &ptr, 1); + ipc_msg_hdr *hdr; + DNSServiceErrorType err = ConnectToServer(&tmp, 0, setdomain_request, NULL, NULL, NULL); + if (err) return err; + + hdr = create_hdr(setdomain_request, &len, &ptr, 0, tmp); + if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; } - if (!hdr) return kDNSServiceErr_Unknown; put_flags(flags, &ptr); put_string(domain, &ptr); + err = deliver_request(hdr, tmp); // Will free hdr for us + DNSServiceRefDeallocate(tmp); + return err; +} - 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; +static void handle_regservice_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName]; - 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); - } + get_string(&data, end, name, 256); + get_string(&data, end, regtype, kDNSServiceMaxDomainName); + get_string(&data, end, domain, kDNSServiceMaxDomainName); + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_regservice_response: error reading result from daemon"); + else ((DNSServiceRegisterReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_err, name, regtype, domain, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +} DNSServiceErrorType DNSSD_API DNSServiceRegister - ( +( DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *name, const char *regtype, const char *domain, const char *host, - uint16_t PortInNetworkByteOrder, - uint16_t txtLen, + uint16_t PortInNetworkByteOrder, + uint16_t txtLen, const void *txtRecord, - DNSServiceRegisterReply callBack, + DNSServiceRegisterReply callBack, void *context - ) - { - char *msg = NULL, *ptr; +) +{ + char *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 + // No callback must have auto-rename if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam; + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + err = ConnectToServer(sdRef, flags, reg_service_request, callBack ? handle_regservice_response : NULL, (void *)callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + len = sizeof(DNSServiceFlags); len += sizeof(uint32_t); // interfaceIndex len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4; len += 2 * sizeof(uint16_t); // port, txtLen len += txtLen; - hdr = create_hdr(reg_service_request, &len, &ptr, 1); - if (!hdr) goto error; - if (!callBack) hdr->flags |= IPC_FLAGS_NOREPLY; - msg = (char *)hdr; + hdr = create_hdr(reg_service_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + // If it is going over a shared connection, then don't set the IPC_FLAGS_NOREPLY + // as it affects all the operations over the shared connection. This is not + // a normal case and hence receiving the response back from the daemon and + // discarding it in ConnectionResponse is okay. + + if (!(flags & kDNSServiceFlagsShareConnection) && !callBack) hdr->ipc_flags |= IPC_FLAGS_NOREPLY; + put_flags(flags, &ptr); - put_long(interfaceIndex, &ptr); + put_uint32(interfaceIndex, &ptr); put_string(name, &ptr); put_string(regtype, &ptr); put_string(domain, &ptr); put_string(host, &ptr); *ptr++ = port.b[0]; *ptr++ = port.b[1]; - put_short(txtLen, &ptr); + put_uint16(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; - + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } 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; +static void handle_enumeration_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ 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); - } + get_string(&data, end, domain, kDNSServiceMaxDomainName); + if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_enumeration_response: error reading result from daemon"); + else ((DNSServiceDomainEnumReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, domain, sdr->AppContext); + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +} DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains - ( - DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceDomainEnumReply callBack, - void *context - ) - { - char *msg = NULL, *ptr; +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context +) +{ + char *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; + err = ConnectToServer(sdRef, flags, enumeration_request, handle_enumeration_response, (void *)callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL - len = sizeof(DNSServiceFlags); + len = sizeof(DNSServiceFlags); len += sizeof(uint32_t); - hdr = create_hdr(enumeration_request, &len, &ptr, 1); - if (!hdr) goto error; - msg = (void *)hdr; + hdr = create_hdr(enumeration_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } put_flags(flags, &ptr); - put_long(interfaceIndex, &ptr); + put_uint32(interfaceIndex, &ptr); - sdr = connect_to_server(); - if (!sdr) goto error; - err = deliver_request(msg, sdr, 1); - if (err) + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *const data, const char *const end) +{ + (void)data; // Unused + + //printf("ConnectionResponse got %d\n", cbh->ipc_hdr.op); + if (cbh->ipc_hdr.op != reg_record_reply_op) + { + // When using kDNSServiceFlagsShareConnection, need to search the list of associated DNSServiceOps + // to find the one this response is intended for, and then call through to its ProcessReply handler. + // We start with our first subordinate DNSServiceRef -- don't want to accidentally match the parent DNSServiceRef. + DNSServiceOp *op = sdr->next; + while (op && (op->uid.u32[0] != cbh->ipc_hdr.client_context.u32[0] || op->uid.u32[1] != cbh->ipc_hdr.client_context.u32[1])) + op = op->next; + // Note: We may sometimes not find a matching DNSServiceOp, in the case where the client has + // cancelled the subordinate DNSServiceOp, but there are still messages in the pipeline from the daemon + if (op && op->ProcessReply) op->ProcessReply(op, cbh, data, end); + // WARNING: Don't touch op or sdr after this -- client may have called DNSServiceRefDeallocate + return; + } + else + { + DNSRecordRef rec; + for (rec = sdr->rec; rec; rec = rec->recnext) { - DNSServiceRefDeallocate(sdr); - return err; + if (rec->uid.u32[0] == cbh->ipc_hdr.client_context.u32[0] && rec->uid.u32[1] == cbh->ipc_hdr.client_context.u32[1]) + break; } + // The record might have been freed already and hence not an + // error if the record is not found. + if (!rec) + { + syslog(LOG_INFO, "ConnectionResponse: Record not found"); + return; + } + if (rec->sdr != sdr) + { + syslog(LOG_WARNING, "ConnectionResponse: Record sdr mismatch: rec %p sdr %p", rec->sdr, sdr); + return; + } + + if (sdr->op == connection_request || sdr->op == connection_delegate_request) + { + rec->AppCallback(rec->sdr, rec, cbh->cb_flags, cbh->cb_err, rec->AppContext); + } + else + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectionResponse: sdr->op != connection_request"); + rec->AppCallback(rec->sdr, rec, 0, kDNSServiceErr_Unknown, rec->AppContext); + } + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function + } +} - sdr->op = enumeration_request; - sdr->process_reply = handle_enumeration_response; - sdr->app_callback = callBack; - sdr->app_context = context; - *sdRef = sdr; +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) +{ + char *ptr; + size_t len = 0; + ipc_msg_hdr *hdr; + DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_request, ConnectionResponse, NULL, NULL); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + hdr = create_hdr(connection_request, &len, &ptr, 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } return err; +} -error: - if (msg) free(msg); - if (*sdRef) { free(*sdRef); *sdRef = NULL; } - return kDNSServiceErr_Unknown; +#if APPLE_OSX_mDNSResponder && !TARGET_IPHONE_SIMULATOR +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid) +{ + char *ptr; + size_t len = 0; + ipc_msg_hdr *hdr; + + DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_delegate_request, ConnectionResponse, NULL, NULL); + if (err) + { + return err; // On error ConnectToServer leaves *sdRef set to NULL } -static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + // Only one of the two options can be set. If pid is zero, uuid is used. + // If both are specified only pid will be used. We send across the pid + // so that the daemon knows what to read from the socket. + + len += sizeof(int32_t); + + hdr = create_hdr(connection_delegate_request, &len, &ptr, 0, *sdRef); + if (!hdr) { - DNSServiceFlags flags; - uint32_t interfaceIndex; - DNSServiceErrorType errorCode; - DNSRecordRef rref = hdr->client_context.context; + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoMemory; + } - 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); + if (pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED, &pid, sizeof(pid)) == -1) + { + syslog(LOG_WARNING, "dnssdclientstub: Could not setsockopt() for PID[%d], no entitlements or process(pid) invalid errno:%d (%s)", pid, errno, strerror(errno)); + // Free the hdr in case we return before calling deliver_request() + if (hdr) + free(hdr); + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoAuth; + } - rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context); + if (!pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED_UUID, uuid, sizeof(uuid_t)) == -1) + { + syslog(LOG_WARNING, "dnssdclientstub: Could not setsockopt() for UUID, no entitlements or process(uuid) invalid errno:%d (%s) ", errno, strerror(errno)); + // Free the hdr in case we return before calling deliver_request() + if (hdr) + free(hdr); + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoAuth; } -DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) + put_uint32(pid, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { - 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; + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; } + return err; +} +#elif TARGET_IPHONE_SIMULATOR // This hack is for Simulator platform only +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid) +{ + (void) pid; + (void) uuid; + return DNSServiceCreateConnection(sdRef); +} +#endif DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord - ( - DNSServiceRef sdRef, +( + DNSServiceRef sdRef, DNSRecordRef *RecordRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, const void *rdata, - uint32_t ttl, - DNSServiceRegisterRecordReply callBack, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, void *context - ) - { - char *msg = NULL, *ptr; +) +{ + char *ptr; size_t len; ipc_msg_hdr *hdr = NULL; - DNSServiceRef tmp = NULL; DNSRecordRef rref = NULL; + DNSRecord **p; int f1 = (flags & kDNSServiceFlagsShared) != 0; int f2 = (flags & kDNSServiceFlagsUnique) != 0; if (f1 + f2 != 1) return kDNSServiceErr_BadParam; - if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0) + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) + flags |= kDNSServiceFlagsIncludeP2P; + + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); return kDNSServiceErr_BadReference; + } + + if (sdRef->op != connection_request && sdRef->op != connection_delegate_request) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op); + return kDNSServiceErr_BadReference; + } + *RecordRef = NULL; - len = sizeof(DNSServiceFlags); + len = 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; + // Bump up the uid. Normally for shared operations (kDNSServiceFlagsShareConnection), this + // is done in ConnectToServer. For DNSServiceRegisterRecord, ConnectToServer has already + // been called. As multiple DNSServiceRegisterRecords can be multiplexed over a single + // connection, we need a way to demultiplex the response so that the callback corresponding + // to the right DNSServiceRegisterRecord instance can be called. Use the same mechanism that + // is used by kDNSServiceFlagsShareConnection. create_hdr copies the uid value to ipc + // hdr->client_context which will be returned in the ipc response. + if (++sdRef->uid.u32[0] == 0) + ++sdRef->uid.u32[1]; + hdr = create_hdr(reg_record_request, &len, &ptr, 1, sdRef); + if (!hdr) return kDNSServiceErr_NoMemory; + put_flags(flags, &ptr); - put_long(interfaceIndex, &ptr); + put_uint32(interfaceIndex, &ptr); put_string(fullname, &ptr); - put_short(rrtype, &ptr); - put_short(rrclass, &ptr); - put_short(rdlen, &ptr); + put_uint16(rrtype, &ptr); + put_uint16(rrclass, &ptr); + put_uint16(rdlen, &ptr); put_rdata(rdlen, rdata, &ptr); - put_long(ttl, &ptr); + put_uint32(ttl, &ptr); - rref = malloc(sizeof(_DNSRecordRef_t)); - if (!rref) goto error; - rref->app_context = context; - rref->app_callback = callBack; + rref = malloc(sizeof(DNSRecord)); + if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; } + rref->AppContext = context; + rref->AppCallback = callBack; rref->record_index = sdRef->max_index++; rref->sdr = sdRef; + rref->recnext = NULL; *RecordRef = rref; - hdr->client_context.context = rref; + // Remember the uid that we are sending across so that we can match + // when the response comes back. + rref->uid = sdRef->uid; hdr->reg_index = rref->record_index; - return deliver_request(msg, sdRef, 0); + p = &(sdRef)->rec; + while (*p) p = &(*p)->recnext; + *p = rref; -error: - if (rref) free(rref); - if (tmp) free(tmp); - if (hdr) free(hdr); - return kDNSServiceErr_Unknown; - } + return deliver_request(hdr, sdRef); // Will free hdr for us +} -//sdRef returned by DNSServiceRegister() +// sdRef returned by DNSServiceRegister() DNSServiceErrorType DNSSD_API DNSServiceAddRecord - ( - DNSServiceRef sdRef, +( + DNSServiceRef sdRef, DNSRecordRef *RecordRef, - DNSServiceFlags flags, - uint16_t rrtype, - uint16_t rdlen, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, const void *rdata, - uint32_t ttl - ) - { + uint32_t ttl +) +{ ipc_msg_hdr *hdr; size_t len = 0; char *ptr; DNSRecordRef rref; + DNSRecord **p; - if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef) + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + if (!RecordRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSRecordRef pointer"); return kDNSServiceErr_BadParam; } + if (sdRef->op != reg_service_request) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with non-DNSServiceRegister DNSServiceRef %p %d", sdRef, sdRef->op); + return kDNSServiceErr_BadReference; + } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); return kDNSServiceErr_BadReference; + } + *RecordRef = NULL; - len += 2 * sizeof(uint16_t); //rrtype, rdlen + len += 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; + hdr = create_hdr(add_record_request, &len, &ptr, 1, sdRef); + if (!hdr) return kDNSServiceErr_NoMemory; put_flags(flags, &ptr); - put_short(rrtype, &ptr); - put_short(rdlen, &ptr); + put_uint16(rrtype, &ptr); + put_uint16(rdlen, &ptr); put_rdata(rdlen, rdata, &ptr); - put_long(ttl, &ptr); + put_uint32(ttl, &ptr); - rref = malloc(sizeof(_DNSRecordRef_t)); - if (!rref) goto error; - rref->app_context = NULL; - rref->app_callback = NULL; + rref = malloc(sizeof(DNSRecord)); + if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; } + rref->AppContext = NULL; + rref->AppCallback = NULL; rref->record_index = sdRef->max_index++; rref->sdr = sdRef; + rref->recnext = NULL; *RecordRef = rref; - hdr->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; + p = &(sdRef)->rec; + while (*p) p = &(*p)->recnext; + *p = rref; + + return deliver_request(hdr, sdRef); // Will free hdr for us } -//DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord +// DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, - DNSServiceFlags flags, - uint16_t rdlen, +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + uint16_t rdlen, const void *rdata, - uint32_t ttl - ) - { + uint32_t ttl +) +{ ipc_msg_hdr *hdr; size_t len = 0; char *ptr; - if (!sdRef) return kDNSServiceErr_BadReference; + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); + return kDNSServiceErr_BadReference; + } + + // Note: RecordRef is allowed to be NULL len += sizeof(uint16_t); len += rdlen; len += sizeof(uint32_t); len += sizeof(DNSServiceFlags); - hdr = create_hdr(update_record_request, &len, &ptr, 0); - if (!hdr) return kDNSServiceErr_Unknown; + hdr = create_hdr(update_record_request, &len, &ptr, 1, sdRef); + if (!hdr) return kDNSServiceErr_NoMemory; hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX; put_flags(flags, &ptr); - put_short(rdlen, &ptr); + put_uint16(rdlen, &ptr); put_rdata(rdlen, rdata, &ptr); - put_long(ttl, &ptr); - return deliver_request((char *)hdr, sdRef, 0); - } + put_uint32(ttl, &ptr); + return deliver_request(hdr, sdRef); // Will free hdr for us +} DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, - DNSServiceFlags flags - ) - { +( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags +) +{ ipc_msg_hdr *hdr; size_t len = 0; char *ptr; DNSServiceErrorType err; - if (!sdRef || !RecordRef || !sdRef->max_index) + if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + if (!RecordRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSRecordRef"); return kDNSServiceErr_BadParam; } + if (!sdRef->max_index) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with bad DNSServiceRef"); return kDNSServiceErr_BadReference; } + + if (!DNSServiceRefValid(sdRef)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator); return kDNSServiceErr_BadReference; + } len += sizeof(flags); - hdr = create_hdr(remove_record_request, &len, &ptr, 0); - if (!hdr) return kDNSServiceErr_Unknown; + hdr = create_hdr(remove_record_request, &len, &ptr, 1, sdRef); + if (!hdr) return kDNSServiceErr_NoMemory; hdr->reg_index = RecordRef->record_index; put_flags(flags, &ptr); - err = deliver_request((char *)hdr, sdRef, 0); - if (!err) free(RecordRef); - return err; + err = deliver_request(hdr, sdRef); // Will free hdr for us + if (!err) + { + // This RecordRef could have been allocated in DNSServiceRegisterRecord or DNSServiceAddRecord. + // If so, delink from the list before freeing + DNSRecord **p = &sdRef->rec; + while (*p && *p != RecordRef) p = &(*p)->recnext; + if (*p) *p = RecordRef->recnext; + free(RecordRef); } + return err; +} DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord - ( - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, - const void *rdata - ) - { +( + 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; + DNSServiceOp *tmp; + + DNSServiceErrorType err = ConnectToServer(&tmp, flags, reconfirm_record_request, NULL, NULL, NULL); + if (err) return err; len = sizeof(DNSServiceFlags); len += sizeof(uint32_t); len += strlen(fullname) + 1; len += 3 * sizeof(uint16_t); len += rdlen; - tmp = connect_to_server(); - if (!tmp) return(kDNSServiceErr_Unknown); - hdr = create_hdr(reconfirm_record_request, &len, &ptr, 1); - if (!hdr) return(kDNSServiceErr_Unknown); + hdr = create_hdr(reconfirm_record_request, &len, &ptr, 0, tmp); + if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; } put_flags(flags, &ptr); - put_long(interfaceIndex, &ptr); + put_uint32(interfaceIndex, &ptr); put_string(fullname, &ptr); - put_short(rrtype, &ptr); - put_short(rrclass, &ptr); - put_short(rdlen, &ptr); + put_uint16(rrtype, &ptr); + put_uint16(rrclass, &ptr); + put_uint16(rdlen, &ptr); put_rdata(rdlen, rdata, &ptr); - ConvertHeaderBytes(hdr); - write_all(tmp->sockfd, (char *)hdr, (int) len); - free(hdr); + + err = deliver_request(hdr, tmp); // Will free hdr for us DNSServiceRefDeallocate(tmp); - return(kDNSServiceErr_NoError); + return err; +} + + +static void handle_port_mapping_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) +{ + union { uint32_t l; u_char b[4]; } addr; + uint8_t protocol; + union { uint16_t s; u_char b[2]; } internalPort; + union { uint16_t s; u_char b[2]; } externalPort; + uint32_t ttl; + + if (!data || data + 13 > end) goto fail; + + addr.b[0] = *data++; + addr.b[1] = *data++; + addr.b[2] = *data++; + addr.b[3] = *data++; + protocol = *data++; + internalPort.b[0] = *data++; + internalPort.b[1] = *data++; + externalPort.b[0] = *data++; + externalPort.b[1] = *data++; + ttl = get_uint32(&data, end); + if (!data) goto fail; + + ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, addr.l, protocol, internalPort.s, externalPort.s, ttl, sdr->AppContext); + return; + // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function + + fail : + syslog(LOG_WARNING, "dnssd_clientstub handle_port_mapping_response: error reading result from daemon"); +} + +DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + uint32_t protocol, /* TCP and/or UDP */ + uint16_t internalPortInNetworkByteOrder, + uint16_t externalPortInNetworkByteOrder, + uint32_t ttl, /* time to live in seconds */ + DNSServiceNATPortMappingReply callBack, + void *context /* may be NULL */ +) +{ + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + union { uint16_t s; u_char b[2]; } internalPort = { internalPortInNetworkByteOrder }; + union { uint16_t s; u_char b[2]; } externalPort = { externalPortInNetworkByteOrder }; + + DNSServiceErrorType err = ConnectToServer(sdRef, flags, port_mapping_request, handle_port_mapping_response, (void *)callBack, context); + if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += sizeof(protocol); + len += sizeof(internalPort); + len += sizeof(externalPort); + len += sizeof(ttl); + + hdr = create_hdr(port_mapping_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); + if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } + + put_flags(flags, &ptr); + put_uint32(interfaceIndex, &ptr); + put_uint32(protocol, &ptr); + *ptr++ = internalPort.b[0]; + *ptr++ = internalPort.b[1]; + *ptr++ = externalPort.b[0]; + *ptr++ = externalPort.b[1]; + put_uint32(ttl, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } + return err; +} + +#if _DNS_SD_LIBDISPATCH +DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue +( + DNSServiceRef service, + dispatch_queue_t queue +) +{ + int dnssd_fd = DNSServiceRefSockFD(service); + if (dnssd_fd == dnssd_InvalidSocket) return kDNSServiceErr_BadParam; + if (!queue) + { + syslog(LOG_WARNING, "dnssd_clientstub: DNSServiceSetDispatchQueue dispatch queue NULL"); + return kDNSServiceErr_BadParam; + } + if (service->disp_queue) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch queue set already"); + return kDNSServiceErr_BadParam; + } + if (service->disp_source) + { + syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch source set already"); + return kDNSServiceErr_BadParam; + } + service->disp_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, dnssd_fd, 0, queue); + if (!service->disp_source) + { + syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch_source_create failed"); + return kDNSServiceErr_NoMemory; + } + service->disp_queue = queue; + dispatch_source_set_event_handler(service->disp_source, ^{DNSServiceProcessResult(service);}); + dispatch_source_set_cancel_handler(service->disp_source, ^{dnssd_close(dnssd_fd);}); + dispatch_resume(service->disp_source); + return kDNSServiceErr_NoError; +} +#endif // _DNS_SD_LIBDISPATCH + +#if !defined(_WIN32) + +static void DNSSD_API SleepKeepaliveCallback(DNSServiceRef sdRef, DNSRecordRef rec, const DNSServiceFlags flags, + DNSServiceErrorType errorCode, void *context) +{ + SleepKAContext *ka = (SleepKAContext *)context; + (void)rec; // Unused + (void)flags; // Unused + + if (sdRef->kacontext != context) + syslog(LOG_WARNING, "SleepKeepaliveCallback context mismatch"); + + if (ka->AppCallback) + ((DNSServiceSleepKeepaliveReply)ka->AppCallback)(sdRef, errorCode, ka->AppContext); +} + +DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive +( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + int fd, + unsigned int timeout, + DNSServiceSleepKeepaliveReply callBack, + void *context +) +{ + char source_str[INET6_ADDRSTRLEN]; + char target_str[INET6_ADDRSTRLEN]; + struct sockaddr_storage lss; + struct sockaddr_storage rss; + socklen_t len1, len2; + unsigned int len, proxyreclen; + char buf[256]; + DNSServiceErrorType err; + DNSRecordRef record = NULL; + char name[10]; + char recname[128]; + SleepKAContext *ka; + unsigned int i, unique; + + + (void) flags; //unused + if (!timeout) return kDNSServiceErr_BadParam; + + + len1 = sizeof(lss); + if (getsockname(fd, (struct sockaddr *)&lss, &len1) < 0) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getsockname %d\n", errno); + return kDNSServiceErr_BadParam; + } + + len2 = sizeof(rss); + if (getpeername(fd, (struct sockaddr *)&rss, &len2) < 0) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getpeername %d\n", errno); + return kDNSServiceErr_BadParam; } + if (len1 != len2) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive local/remote info not same"); + return kDNSServiceErr_Unknown; + } + + unique = 0; + if (lss.ss_family == AF_INET) + { + struct sockaddr_in *sl = (struct sockaddr_in *)&lss; + struct sockaddr_in *sr = (struct sockaddr_in *)&rss; + unsigned char *ptr = (unsigned char *)&sl->sin_addr; + + if (!inet_ntop(AF_INET, (const void *)&sr->sin_addr, target_str, sizeof (target_str))) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote info failed %d", errno); + return kDNSServiceErr_Unknown; + } + if (!inet_ntop(AF_INET, (const void *)&sl->sin_addr, source_str, sizeof (source_str))) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive local info failed %d", errno); + return kDNSServiceErr_Unknown; + } + // Sum of all bytes in the local address and port should result in a unique + // number in the local network + for (i = 0; i < sizeof(struct in_addr); i++) + unique += ptr[i]; + unique += sl->sin_port; + len = snprintf(buf+1, sizeof(buf) - 1, "t=%u h=%s d=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl->sin_port), ntohs(sr->sin_port)); + } + else + { + struct sockaddr_in6 *sl6 = (struct sockaddr_in6 *)&lss; + struct sockaddr_in6 *sr6 = (struct sockaddr_in6 *)&rss; + unsigned char *ptr = (unsigned char *)&sl6->sin6_addr; + + if (!inet_ntop(AF_INET6, (const void *)&sr6->sin6_addr, target_str, sizeof (target_str))) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote6 info failed %d", errno); + return kDNSServiceErr_Unknown; + } + if (!inet_ntop(AF_INET6, (const void *)&sl6->sin6_addr, source_str, sizeof (source_str))) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive local6 info failed %d", errno); + return kDNSServiceErr_Unknown; + } + for (i = 0; i < sizeof(struct in6_addr); i++) + unique += ptr[i]; + unique += sl6->sin6_port; + len = snprintf(buf+1, sizeof(buf) - 1, "t=%u H=%s D=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl6->sin6_port), ntohs(sr6->sin6_port)); + } + + if (len >= (sizeof(buf) - 1)) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit local/remote info"); + return kDNSServiceErr_Unknown; + } + // Include the NULL byte also in the first byte. The total length of the record includes the + // first byte also. + buf[0] = len + 1; + proxyreclen = len + 2; + + len = snprintf(name, sizeof(name), "%u", unique); + if (len >= sizeof(name)) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit unique"); + return kDNSServiceErr_Unknown; + } + + len = snprintf(recname, sizeof(recname), "%s.%s", name, "_keepalive._dns-sd._udp.local"); + if (len >= sizeof(recname)) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit name"); + return kDNSServiceErr_Unknown; + } + + ka = malloc(sizeof(SleepKAContext)); + if (!ka) return kDNSServiceErr_NoMemory; + ka->AppCallback = (void *)callBack; + ka->AppContext = context; + + err = DNSServiceCreateConnection(sdRef); + if (err) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection"); + free(ka); + return err; + } + + // we don't care about the "record". When sdRef gets deallocated later, it will be freed too + err = DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, 0, recname, + kDNSServiceType_NULL, kDNSServiceClass_IN, proxyreclen, buf, kDNSServiceInterfaceIndexAny, SleepKeepaliveCallback, ka); + if (err) + { + syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection"); + free(ka); + return err; + } + (*sdRef)->kacontext = ka; + return kDNSServiceErr_NoError; +} +#endif diff --git a/usr/src/lib/libdns_sd/common/dnssd_ipc.c b/usr/src/lib/libdns_sd/common/dnssd_ipc.c index 92e76bab0c..6059eb392c 100644 --- a/usr/src/lib/libdns_sd/common/dnssd_ipc.c +++ b/usr/src/lib/libdns_sd/common/dnssd_ipc.c @@ -2,134 +2,160 @@ * * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. * - * Redistribution and use in source and binary forms, with or without + * 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. + * 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 + * 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])); - } +#if defined(_WIN32) + +char *win32_strerror(int inErrorCode) +{ + static char buffer[1024]; + DWORD n; + memset(buffer, 0, sizeof(buffer)); + n = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + (DWORD) inErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer, + sizeof(buffer), + NULL); + if (n > 0) + { + // Remove any trailing CR's or LF's since some messages have them. + while ((n > 0) && isspace(((unsigned char *) buffer)[n - 1])) + buffer[--n] = '\0'; + } + return buffer; +} + +#endif + +void put_uint32(const uint32_t l, char **ptr) +{ + (*ptr)[0] = (char)((l >> 24) & 0xFF); + (*ptr)[1] = (char)((l >> 16) & 0xFF); + (*ptr)[2] = (char)((l >> 8) & 0xFF); + (*ptr)[3] = (char)((l ) & 0xFF); + *ptr += sizeof(uint32_t); +} + +uint32_t get_uint32(const char **ptr, const char *end) +{ + if (!*ptr || *ptr + sizeof(uint32_t) > end) + { + *ptr = NULL; + return(0); + } + else + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint32_t); + return((uint32_t) ((uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3])); + } +} + +void put_uint16(uint16_t s, char **ptr) +{ + (*ptr)[0] = (char)((s >> 8) & 0xFF); + (*ptr)[1] = (char)((s ) & 0xFF); + *ptr += sizeof(uint16_t); +} + +uint16_t get_uint16(const char **ptr, const char *end) +{ + if (!*ptr || *ptr + sizeof(uint16_t) > end) + { + *ptr = NULL; + return(0); + } + else + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint16_t); + return((uint16_t) ((uint16_t)p[0] << 8 | p[1])); + } +} int put_string(const char *str, char **ptr) - { - if (!str) str = ""; - strcpy(*ptr, str); - *ptr += strlen(str) + 1; - return 0; - } - -int get_string(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; - } +{ + if (!str) str = ""; + strcpy(*ptr, str); + *ptr += strlen(str) + 1; + return 0; +} + +int get_string(const char **ptr, const char *const end, char *buffer, int buflen) +{ + if (!*ptr) + { + *buffer = 0; + return(-1); + } + else + { + char *lim = buffer + buflen; // Calculate limit + while (*ptr < end && buffer < lim) + { + char c = *buffer++ = *(*ptr)++; + if (c == 0) return(0); // Success + } + if (buffer == lim) buffer--; + *buffer = 0; // Failed, so terminate string, + *ptr = NULL; // clear pointer, + return(-1); // and return failure indication + } +} void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr) - { - memcpy(*ptr, rdata, rdlen); - *ptr += rdlen; - } - -char *get_rdata(char **ptr, int rdlen) - { - char *rd = *ptr; - *ptr += rdlen; - return rd; - } +{ + memcpy(*ptr, rdata, rdlen); + *ptr += rdlen; +} + +const char *get_rdata(const char **ptr, const char *end, int rdlen) +{ + if (!*ptr || *ptr + rdlen > end) + { + *ptr = NULL; + return(0); + } + else + { + const char *rd = *ptr; + *ptr += rdlen; + return rd; + } +} void ConvertHeaderBytes(ipc_msg_hdr *hdr) - { - hdr->version = htonl(hdr->version); - hdr->datalen = htonl(hdr->datalen); - hdr->flags = htonl(hdr->flags); - hdr->op = htonl(hdr->op ); - hdr->reg_index = htonl(hdr->reg_index); - } +{ + hdr->version = htonl(hdr->version); + hdr->datalen = htonl(hdr->datalen); + hdr->ipc_flags = htonl(hdr->ipc_flags); + hdr->op = htonl(hdr->op ); + hdr->reg_index = htonl(hdr->reg_index); +} diff --git a/usr/src/lib/libdns_sd/common/dnssd_ipc.h b/usr/src/lib/libdns_sd/common/dnssd_ipc.h index 2f76591f9a..ec098032b4 100644 --- a/usr/src/lib/libdns_sd/common/dnssd_ipc.h +++ b/usr/src/lib/libdns_sd/common/dnssd_ipc.h @@ -2,173 +2,126 @@ * * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. * - * Redistribution and use in source and binary forms, with or without + * 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. + * 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 + * 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 +# include <winsock2.h> +# define dnssd_InvalidSocket INVALID_SOCKET +# define dnssd_SocketValid(s) ((s) != INVALID_SOCKET) +# define dnssd_EWOULDBLOCK WSAEWOULDBLOCK +# define dnssd_EINTR WSAEINTR +# define dnssd_ECONNRESET WSAECONNRESET +# define dnssd_sock_t SOCKET +# define dnssd_socklen_t int +# define dnssd_close(sock) closesocket(sock) +# define dnssd_errno WSAGetLastError() +# define dnssd_strerror(X) win32_strerror(X) +# define ssize_t int +# define getpid _getpid +# define unlink _unlink +extern char *win32_strerror(int inErrorCode); #else -# include <sys/types.h> -# include <unistd.h> -# 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 +# 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> +# include <arpa/inet.h> +# define dnssd_InvalidSocket -1 +# define dnssd_SocketValid(s) ((s) >= 0) +# define dnssd_EWOULDBLOCK EWOULDBLOCK +# define dnssd_EINTR EINTR +# define dnssd_ECONNRESET ECONNRESET +# define dnssd_EPIPE EPIPE +# define dnssd_sock_t int +# define dnssd_socklen_t unsigned int +# define dnssd_close(sock) close(sock) +# define dnssd_errno errno +# define dnssd_strerror(X) strerror(X) #endif #if defined(USE_TCP_LOOPBACK) -# define AF_DNSSD AF_INET -# define MDNS_TCP_SERVERADDR "127.0.0.1" -# define MDNS_TCP_SERVERPORT 5354 -# define LISTENQ 5 -# define dnssd_sockaddr_t struct sockaddr_in +# 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 +# define AF_DNSSD AF_LOCAL +# ifndef MDNS_UDS_SERVERPATH +# define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder" +# endif +# define MDNS_UDS_SERVERPATH_ENVVAR "DNSSD_UDS_PATH" +# 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 +#define AF_LOCAL AF_UNIX #endif // General UDS constants -#define TXT_RECORD_INDEX ((uint32_t)(-1)) // record index for default text record +#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 +#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client + +// Structure packing macro. If we're not using GNUC, it's not fatal. Most compilers naturally pack the on-the-wire +// structures correctly anyway, so a plain "struct" is usually fine. In the event that structures are not packed +// correctly, our compile-time assertion checks will catch it and prevent inadvertent generation of non-working code. +#ifndef packedstruct + #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #define packedstruct struct __attribute__((__packed__)) + #define packedunion union __attribute__((__packed__)) + #else + #define packedstruct struct + #define packedunion union + #endif +#endif typedef enum - { - connection = 1, // connected socket via DNSServiceConnect() - reg_record_request, // reg/remove record only valid for connected sockets +{ + request_op_none = 0, // No request yet received on this connection + connection_request = 1, // connected socket via DNSServiceConnect() + reg_record_request, // reg/remove record only valid for connected sockets remove_record_request, enumeration_request, reg_service_request, @@ -178,76 +131,96 @@ typedef enum reconfirm_record_request, add_record_request, update_record_request, - setdomain_request - } request_op_t; + setdomain_request, // Up to here is in Tiger and B4W 1.0.3 + getproperty_request, // New in B4W 1.0.4 + port_mapping_request, // New in Leopard and B4W 2.0 + addrinfo_request, + send_bpf, // New in SL + getpid_request, + release_request, + connection_delegate_request, + + cancel_request = 63 +} request_op_t; typedef enum - { +{ enumeration_reply_op = 64, reg_service_reply_op, browse_reply_op, resolve_reply_op, query_reply_op, - reg_record_reply_op - } 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 - ); + reg_record_reply_op, // Up to here is in Tiger and B4W 1.0.3 + getproperty_reply_op, // New in B4W 1.0.4 + port_mapping_reply_op, // New in Leopard and B4W 2.0 + addrinfo_reply_op +} reply_op_t; + +#if defined(_WIN64) +# pragma pack(push,4) +#elif !defined(__GNUC__) +# pragma pack(1) +#endif -// allow 64-bit client to interoperate w/ 32-bit daemon -typedef union - { +// Define context object big enough to hold a 64-bit pointer, +// to accomodate 64-bit clients communicating with 32-bit daemon. +// There's no reason for the daemon to ever be a 64-bit process, but its clients might be +typedef packedunion +{ void *context; - uint32_t ptr64[2]; - } client_context_t; + uint32_t u32[2]; +} client_context_t; -typedef struct ipc_msg_hdr_struct - { +typedef packedstruct +{ uint32_t version; uint32_t datalen; - uint32_t flags; - uint32_t op; // request_op_t or reply_op_t + uint32_t ipc_flags; + uint32_t op; // request_op_t or reply_op_t client_context_t client_context; // context passed from client, returned by server in corresponding reply uint32_t reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a - // socket connected by DNSServiceConnect(). Must be unique in the scope of the connection, such that and + // socket connected by DNSServiceCreateConnection(). Must be unique in the scope of the connection, such that and // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord()) - uint32_t padbytes; - } ipc_msg_hdr_struct; +} ipc_msg_hdr; + +#if defined(_WIN64) +# pragma pack(pop) +#elif !defined(__GNUC__) +# pragma pack() +#endif -// 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_uint32(const uint32_t l, char **ptr); +uint32_t get_uint32(const char **ptr, const char *end); -void put_short(uint16_t s, char **ptr); -uint16_t get_short(char **ptr); +void put_uint16(uint16_t s, char **ptr); +uint16_t get_uint16(const char **ptr, const char *end); -#define put_flags put_long -#define get_flags get_long +#define put_flags put_uint32 +#define get_flags get_uint32 -#define put_error_code put_long -#define get_error_code get_long +#define put_error_code put_uint32 +#define get_error_code get_uint32 int put_string(const char *str, char **ptr); -int get_string(char **ptr, char *buffer, int buflen); +int get_string(const char **ptr, const char *const end, char *buffer, int buflen); void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr); -char *get_rdata(char **ptr, int rdlen); // return value is rdata pointed to by *ptr - - // rdata is not copied from buffer. +const char *get_rdata(const char **ptr, const char *end, int rdlen); // return value is rdata pointed to by *ptr - +// rdata is not copied from buffer. void ConvertHeaderBytes(ipc_msg_hdr *hdr); +struct CompileTimeAssertionChecks_dnssd_ipc +{ + // Check that the compiler generated our on-the-wire packet format structure definitions + // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. + char assert0[(sizeof(client_context_t) == 8) ? 1 : -1]; + char assert1[(sizeof(ipc_msg_hdr) == 28) ? 1 : -1]; +}; + #endif // DNSSD_IPC_H diff --git a/usr/src/lib/libdns_sd/common/mapfile-vers b/usr/src/lib/libdns_sd/common/mapfile-vers index f714e0ef36..53ab932234 100644 --- a/usr/src/lib/libdns_sd/common/mapfile-vers +++ b/usr/src/lib/libdns_sd/common/mapfile-vers @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# +# Copyright 2016 Toomas Soome <tsoome@me.com> # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # @@ -38,6 +38,14 @@ $mapfile_version 2 +SYMBOL_VERSION ILLUMOS_0.1 { # mDNSResponder-576.30.4 + global: + DNSServiceGetAddrInfo; + DNSServiceGetProperty; + DNSServiceNATPortMappingCreate; + DNSServiceSleepKeepalive; +} SUNW_1.1; + SYMBOL_VERSION SUNW_1.1 { global: DNSServiceAddRecord; 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 index 689cb8ba57..b99d341c2f 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java @@ -13,21 +13,6 @@ * 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" - */ 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 index 0ae9b938ba..b92b9dc59f 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java @@ -13,21 +13,6 @@ * 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" - */ 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 index 6caf3a7791..a853d091fa 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java @@ -13,22 +13,6 @@ * 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" - */ 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 index 9841c9492b..f749a88ead 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java @@ -5,60 +5,20 @@ * 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; @@ -67,19 +27,19 @@ 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 + 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> + For example, in this program<P> <PRE><CODE> class MyClient implements BrowseListener { void lookForWebServers() { - myBrowser = DNSSD.browse("_http_.tcp", this); + myBrowser = DNSSD.browse("_http._tcp", this); } - public void serviceFound(DNSSDService browser, int flags, int ifIndex, + 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). @@ -91,14 +51,14 @@ abstract public class DNSSD 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 + /** 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 ); @@ -117,7 +77,7 @@ abstract public class DNSSD 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; + public static final int MAX_DOMAIN_NAME = 1009; /** Pass for ifIndex to specify all available interfaces. */ public static final int ALL_INTERFACES = 0; @@ -125,7 +85,7 @@ abstract public class DNSSD /** Pass for ifIndex to specify the localhost interface. */ public static final int LOCALHOST_ONLY = -1; - /** Browse for instances of a service.<P> + /** Browse for instances of a service.<P> Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P> @@ -139,12 +99,12 @@ abstract public class DNSSD 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 + 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 + Most applications will not specify a domain, instead browsing on the default domain(s). <P> @param listener @@ -159,10 +119,10 @@ abstract public class DNSSD throws DNSSDException { return getInstance()._makeBrowser( flags, ifIndex, regType, domain, listener); } - /** Browse for instances of a service. Use default flags, ifIndex and domain.<P> + /** 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 + 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 @@ -177,16 +137,16 @@ abstract public class DNSSD 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> + /** 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 + 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 + 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 + 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 @@ -194,7 +154,7 @@ abstract public class DNSSD <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. + 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> @@ -202,8 +162,8 @@ abstract public class DNSSD 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". + 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 @@ -217,54 +177,54 @@ abstract public class DNSSD @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, + 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> + /** 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 + 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 + 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". + 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 + 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 + 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 + 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. <length byte> <data> + The txt record rdata. May be null. Note that a non-null txtRecord + MUST be a properly formatted DNS TXT record, i.e. <length byte> <data> <length byte> <data> ... <P> @param listener @@ -275,26 +235,26 @@ abstract public class DNSSD @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, + 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> + /** 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 + 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". + 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 + 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 @@ -309,8 +269,8 @@ abstract public class DNSSD 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> + /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of + multiple individual records.<P> <P> @return A {@link DNSSDRecordRegistrar} that can be used to register records. @@ -321,14 +281,14 @@ abstract public class DNSSD throws DNSSDException { return getInstance()._createRecordRegistrar( listener); } - /** Query for an arbitrary DNS record.<P> + /** 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 + 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> @@ -340,7 +300,7 @@ abstract public class DNSSD as defined in nameser.h. <P> @param rrclass - The class of the resource record, as defined in nameser.h + The class of the resource record, as defined in nameser.h (usually 1 for the Internet class). <P> @param listener @@ -351,12 +311,12 @@ abstract public class DNSSD @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, + 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> + /** 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> @@ -368,8 +328,8 @@ abstract public class DNSSD @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. + 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. @@ -383,15 +343,15 @@ abstract public class DNSSD 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> + /** 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"). + 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. @@ -405,10 +365,10 @@ abstract public class DNSSD 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> + /** 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 + 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. @@ -416,7 +376,7 @@ abstract public class DNSSD @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 + 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> @@ -435,11 +395,11 @@ abstract public class DNSSD @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, + 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> + /** Return the canonical name of a particular interface index.<P> @param ifIndex A valid interface index. Must not be ALL_INTERFACES. <P> @@ -451,7 +411,7 @@ abstract public class DNSSD public static String getNameForIfIndex( int ifIndex) { return getInstance()._getNameForIfIndex( ifIndex); } - /** Return the index of a named interface.<P> + /** Return the index of a named interface.<P> @param ifName A valid interface name. An example is java.net.NetworkInterface.getName(). <P> @@ -466,29 +426,29 @@ abstract public class DNSSD protected DNSSD() {} // prevent direct instantiation /** Return the single instance of DNSSD. */ - static protected final DNSSD getInstance() + static protected final DNSSD getInstance() { SecurityManager sm = System.getSecurityManager(); - if ( sm != null) - sm.checkPermission( new RuntimePermission( "getDNSSDInstance")); - return fInstance; + 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, + 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, + 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, + abstract protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener listener) throws DNSSDException; @@ -498,7 +458,7 @@ abstract public class DNSSD abstract protected String _constructFullName( String serviceName, String regType, String domain) throws DNSSDException; - abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata); abstract protected String _getNameForIfIndex( int ifIndex); @@ -510,11 +470,11 @@ abstract public class DNSSD static { try - { + { String name = System.getProperty( "com.apple.dnssd.DNSSD" ); - if( name == null ) + if (name == null) name = "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class. - fInstance = (DNSSD) Class.forName( name ).newInstance(); + fInstance = (DNSSD) Class.forName(name).newInstance(); } catch( Exception e ) { @@ -559,10 +519,13 @@ class AppleDNSSDException extends DNSSDException "BADTIME", "BADSIG", "BADKEY", - "TRANSIENT" + "TRANSIENT", + "SERVICENOTRUNNING", + "NATPORTMAPPINGUNSUPPORTED", + "NATPORTMAPPINGDISABLED" }; - if ( fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length)) + if (fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length)) { return "DNS-SD Error " + String.valueOf( fErrorCode) + ": " + kMessages[ UNKNOWN - fErrorCode]; } @@ -580,9 +543,9 @@ class AppleDNSSD extends DNSSD { System.loadLibrary( "jdns_sd"); - int libInitResult = InitLibrary( 1); + int libInitResult = InitLibrary( 2); // Current version number (must be sync'd with jnilib version) - if ( libInitResult != DNSSDException.NO_ERROR) + if (libInitResult != DNSSDException.NO_ERROR) throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult).getMessage()); } @@ -594,18 +557,18 @@ class AppleDNSSD extends DNSSD return new AppleBrowser( flags, ifIndex, regType, domain, client); } - protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, + 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, + 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, + return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port, ( txtRecord != null) ? txtRecord.getRawBytes() : null, client); } @@ -615,7 +578,7 @@ class AppleDNSSD extends DNSSD return new AppleRecordRegistrar( listener); } - protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener client) throws DNSSDException { @@ -634,13 +597,13 @@ class AppleDNSSD extends DNSSD 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) + if (rc != 0) throw new AppleDNSSDException( rc); return responseHolder[0]; } - protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata) { ReconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); @@ -659,7 +622,7 @@ class AppleDNSSD extends DNSSD protected native int ConstructName( String serviceName, String regType, String domain, String[] pOut); - protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata); protected native String GetNameForIfIndex( int ifIndex); @@ -685,7 +648,7 @@ class AppleService implements DNSSDService, Runnable protected void ThrowOnErr( int rc) throws DNSSDException { - if ( rc != 0) + if (rc != 0) throw new AppleDNSSDException( rc); } @@ -734,12 +697,12 @@ class AppleService implements DNSSDService, Runnable class AppleBrowser extends AppleService { - public AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client) + 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) + if (!AppleDNSSD.hasAutoCallbacks) new Thread(this).start(); } @@ -749,27 +712,27 @@ class AppleBrowser extends AppleService class AppleResolver extends AppleService { - public AppleResolver( int flags, int ifIndex, String serviceName, String regType, - String domain, ResolveListener client) + public AppleResolver( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener client) throws DNSSDException - { - super(client); + { + super(client); this.ThrowOnErr( this.CreateResolver( flags, ifIndex, serviceName, regType, domain)); - if ( !AppleDNSSD.hasAutoCallbacks) + 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, + 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; + public AppleDNSRecord( AppleService owner) + { + fOwner = owner; fRecord = 0; // record always starts out empty } @@ -785,12 +748,12 @@ class AppleDNSRecord implements DNSRecord this.ThrowOnErr( this.Remove()); } - protected long fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ? + protected long fRecord; // Really a DNSRecord; sizeof(long) == sizeof(void*) ? protected AppleService fOwner; protected void ThrowOnErr( int rc) throws DNSSDException { - if ( rc != 0) + if (rc != 0) throw new AppleDNSSDException( rc); } @@ -801,13 +764,13 @@ class AppleDNSRecord implements DNSRecord 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) + public AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain, + String host, int port, byte[] txtRecord, RegisterListener client) throws DNSSDException - { - super(client); + { + super(client); this.ThrowOnErr( this.BeginRegister( ifIndex, flags, serviceName, regType, domain, host, port, txtRecord)); - if ( !AppleDNSSD.hasAutoCallbacks) + if (!AppleDNSSD.hasAutoCallbacks) new Thread(this).start(); } @@ -827,7 +790,7 @@ class AppleRegistration extends AppleService implements DNSSDRegistration } // Sets fNativeContext. Returns non-zero on error. - protected native int BeginRegister( int ifIndex, int flags, String serviceName, String regType, + 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. @@ -836,16 +799,16 @@ class AppleRegistration extends AppleService implements DNSSDRegistration class AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar { - public AppleRecordRegistrar( RegisterRecordListener listener) + public AppleRecordRegistrar( RegisterRecordListener listener) throws DNSSDException - { - super(listener); + { + super(listener); this.ThrowOnErr( this.CreateConnection()); - if ( !AppleDNSSD.hasAutoCallbacks) + if (!AppleDNSSD.hasAutoCallbacks) new Thread(this).start(); } - public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, + public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, int rrclass, byte[] rdata, int ttl) throws DNSSDException { @@ -859,19 +822,19 @@ class AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar protected native int CreateConnection(); // Sets fNativeContext. Returns non-zero on error. - protected native int RegisterRecord( int flags, int ifIndex, String fullname, int rrtype, + 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) + public AppleQuery( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener client) throws DNSSDException - { - super(client); + { + super(client); this.ThrowOnErr( this.CreateQuery( flags, ifIndex, serviceName, rrtype, rrclass)); - if ( !AppleDNSSD.hasAutoCallbacks) + if (!AppleDNSSD.hasAutoCallbacks) new Thread(this).start(); } @@ -881,12 +844,12 @@ class AppleQuery extends AppleService class AppleDomainEnum extends AppleService { - public AppleDomainEnum( int flags, int ifIndex, DomainListener client) + public AppleDomainEnum( int flags, int ifIndex, DomainListener client) throws DNSSDException - { - super(client); + { + super(client); this.ThrowOnErr( this.BeginEnum( flags, ifIndex)); - if ( !AppleDNSSD.hasAutoCallbacks) + if (!AppleDNSSD.hasAutoCallbacks) new Thread(this).start(); } 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 index 618128f31f..99549b5d68 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java @@ -13,25 +13,7 @@ * 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; @@ -42,33 +24,39 @@ package com.apple.dnssd; 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; + 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; + public static final int SERVICENOTRUNNING = -65563; + public static final int NATPORTMAPPINGUNSUPPORTED = -65564; + public static final int NATPORTMAPPINGDISABLED = -65565; + + // Note: When adding new error values here, remember also + // to update the corresponding kMessages array in AppleDNSSDException (DNSSD.java) /** 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 index 6983e279fa..569c51dd3a 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java @@ -13,18 +13,6 @@ * 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 */ 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 index 4ae38036ed..720df0b847 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java @@ -13,23 +13,6 @@ * 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. */ 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 index 93cc5fd2c9..10f74021e6 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java @@ -13,21 +13,6 @@ * 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" - */ 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 index 276d5b539a..852f6430e5 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java @@ -13,21 +13,6 @@ * 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" - */ 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 index 7978dda42f..0decb7fc4a 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java @@ -13,21 +13,6 @@ * 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" - */ @@ -38,13 +23,16 @@ package com.apple.dnssd; public interface QueryListener extends BaseListener { - /** Called when a record query has been completed.<P> + /** Called when a record query has been completed. Inspect flags + parameter to determine nature of query event.<P> @param query The active query object. <P> @param flags - Possible values are DNSSD.MORE_COMING. + If kDNSServiceFlagsAdd bit is set, this is a newly discovered answer; + otherwise this is a previously discovered answer which has expired. + Other possible values are DNSSD.MORE_COMING. <P> @param ifIndex The interface on which the query was resolved. (The index for a given 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 index d40b6f3e75..00fa1a6344 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java @@ -13,21 +13,6 @@ * 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" - */ 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 index 6b8c757ae3..6fecf8d81e 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java @@ -13,18 +13,6 @@ * 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" - */ 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 index 4589f53915..33dafa38df 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java @@ -13,22 +13,7 @@ * 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; 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 index c1c5f0b3b7..8d9df7a149 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java @@ -14,29 +14,6 @@ * 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 @@ -126,20 +103,20 @@ public class TXTRecord byte[] oldBytes = fBytes; int valLen = (value != null) ? value.length : 0; int insertion = 0; - byte newLen, avLen; + int newLen, avLen; // locate the insertion point for ( int i=0; i < index && insertion < fBytes.length; i++) - insertion += fBytes[ insertion] + 1; + insertion += (0xFF & (fBytes[ insertion] + 1)); - avLen = (byte) ( keyBytes.length + valLen + (value != null ? 1 : 0)); - newLen = (byte) ( avLen + oldBytes.length + 1); + avLen = keyBytes.length + valLen + (value != null ? 1 : 0); + newLen = 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; + fBytes[ insertion] = ( byte) avLen; System.arraycopy( keyBytes, 0, fBytes, insertion + 1, keyBytes.length); if ( value != null) { @@ -169,7 +146,7 @@ public class TXTRecord return i; } } - avStart += avLen + 1; + avStart += (0xFF & (avLen + 1)); } return -1; } @@ -180,7 +157,7 @@ public class TXTRecord int i, avStart; for ( i=0, avStart=0; avStart < fBytes.length; i++) - avStart += fBytes[ avStart] + 1; + avStart += (0xFF & (fBytes[ avStart] + 1)); return i; } 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 index 381657151b..8f51215165 100644 --- 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 @@ -41,9 +41,6 @@ To do: - display resolved TXTRecord - -ident "%Z%%M% %I% %E% SMI" - */ @@ -57,7 +54,7 @@ import javax.swing.event.*; import com.apple.dnssd.*; -class BrowserApp implements ListSelectionListener, ResolveListener +class BrowserApp implements ListSelectionListener, ResolveListener, Runnable { static BrowserApp app; JFrame frame; @@ -66,6 +63,8 @@ class BrowserApp implements ListSelectionListener, ResolveListener JList domainPane, servicesPane, servicePane; DNSSDService servicesBrowser, serviceBrowser, domainBrowser; JLabel hostLabel, portLabel; + String hostNameForUpdate; + int portForUpdate; public BrowserApp() { @@ -173,22 +172,43 @@ class BrowserApp implements ListSelectionListener, ResolveListener serviceList.getNthServiceName( newSel), serviceList.getNthRegType( newSel), serviceList.getNthDomain( newSel), - new SwingResolveListener( this)); + this); } } } catch ( Exception ex) { terminateWithException( ex); } } + public void run() + { + hostLabel.setText( hostNameForUpdate); + portLabel.setText( String.valueOf( portForUpdate)); + } + 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)); + // We want to update GUI on the AWT event dispatching thread, but we can't stop + // the resolve from that thread, since stop() is synchronized with this callback. + // So, we stop the resolve on this thread, then invokeAndWait on the AWT event thread. + + resolver.stop(); + + hostNameForUpdate = hostName; + portForUpdate = port; + + try { + SwingUtilities.invokeAndWait(this); + } + catch ( Exception e) + { + e.printStackTrace(); + } } public void operationFailed( DNSSDService service, int errorCode) { + service.stop(); // 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 index 472b17f638..7c392a1908 100644 --- 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 @@ -1,3 +1,2 @@ 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 index 62dd11faa5..a1fee5c018 100644 --- 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 @@ -42,9 +42,6 @@ To do: - implement better coloring algorithm - -ident "%Z%%M% %I% %E% SMI" - */ 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 index 71ca6a754e..45c02025c2 100644 --- 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 @@ -1,3 +1,2 @@ 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 index c8ff1e6ca1..db971b2b43 100644 --- 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 @@ -36,11 +36,9 @@ * 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.*; 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 index 988591ccfc..b67313b7bc 100644 --- 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 @@ -36,9 +36,6 @@ * 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" - */ 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 index 1b301ce8e9..fcac75b832 100644 --- 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 @@ -36,9 +36,6 @@ * 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" - */ diff --git a/usr/src/lib/libdns_sd/java/common/JNISupport.c b/usr/src/lib/libdns_sd/java/common/JNISupport.c index bdf453475d..22b40930a2 100644 --- a/usr/src/lib/libdns_sd/java/common/JNISupport.c +++ b/usr/src/lib/libdns_sd/java/common/JNISupport.c @@ -5,78 +5,19 @@ * 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. + 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 @@ -84,8 +25,8 @@ First checked in. // (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 +#ifndef AUTO_CALLBACKS +#define AUTO_CALLBACKS 0 #endif #if !AUTO_CALLBACKS @@ -105,1003 +46,1027 @@ First checked in. #ifdef _WIN32 #include <winsock2.h> #include <iphlpapi.h> -static char * if_indextoname( DWORD ifIndex, char * nameBuff); -static DWORD if_nametoindex( const char * nameStr ); +static char * win32_if_indextoname( DWORD ifIndex, char * nameBuff); +static DWORD win32_if_nametoindex( const char * nameStr ); +#define if_indextoname win32_if_indextoname +#define if_nametoindex win32_if_nametoindex #define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH #else // _WIN32 #include <sys/socket.h> #include <net/if.h> #endif // _WIN32 -#include <jni.h> +// When compiling with "-Wshadow" set, including jni.h produces the following error: +// /System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/jni.h:609: warning: declaration of 'index' shadows a global declaration +// To work around this, we use the preprocessor to map the identifier 'index', which appears harmlessly in function prototype declarations, +// to something 'jni_index', which doesn't conflict +#define index jni_index #include "DNSSD.java.h" +#undef index //#include <syslog.h> -// convenience definition +// convenience definition #ifdef __GNUC__ -#define _UNUSED __attribute__ ((unused)) +#define _UNUSED __attribute__ ((unused)) #else -#define _UNUSED +#define _UNUSED #endif enum { - kInterfaceVersion = 1 // Must match version in .jar file + kInterfaceVersionOne = 1, + kInterfaceVersionCurrent // Must match version in .jar file }; -typedef struct OpContext OpContext; +typedef struct OpContext OpContext; -struct OpContext +struct OpContext { - DNSServiceRef ServiceRef; - JNIEnv *Env; - jobject JavaObj; - jobject ClientObj; - jmethodID Callback; - jmethodID Callback2; + 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; +JavaVM *gJavaVM = NULL; #endif JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv, jclass cls, - jint callerVersion) + jint callerVersion) { - /* Ensure that caller & interface versions match. */ - if ( callerVersion != kInterfaceVersion) - return kDNSServiceErr_Incompatible; + /* Ensure that caller & interface versions match. */ + if ( callerVersion != kInterfaceVersionCurrent) + return kDNSServiceErr_Incompatible; #if AUTO_CALLBACKS - { - jsize numVMs; - - if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs)) - return kDNSServiceErr_BadState; - } + { + jsize numVMs; + + if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs)) + return kDNSServiceErr_BadState; + } #endif - // Set AppleDNSSD.hasAutoCallbacks - { + // Set AppleDNSSD.hasAutoCallbacks + { #if AUTO_CALLBACKS - jboolean hasAutoC = JNI_TRUE; + jboolean hasAutoC = JNI_TRUE; #else - jboolean hasAutoC = JNI_FALSE; + jboolean hasAutoC = JNI_FALSE; #endif - jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z"); - (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC); - } + jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z"); + (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC); + } - return kDNSServiceErr_NoError; + return kDNSServiceErr_NoError; } -static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str) +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; + return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL; } -static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff) +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 ( str != NULL) + (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff); } #if AUTO_CALLBACKS -static void SetupCallbackState( JNIEnv **ppEnv) +static void SetupCallbackState( JNIEnv **ppEnv) { - (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL); + (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL); } -static void TeardownCallbackState( void ) +static void TeardownCallbackState( void ) { - (*gJavaVM)->DetachCurrentThread( gJavaVM); + (*gJavaVM)->DetachCurrentThread( gJavaVM); } -#else // AUTO_CALLBACKS +#else // AUTO_CALLBACKS -static void SetupCallbackState( JNIEnv **ppEnv _UNUSED) +static void SetupCallbackState( JNIEnv **ppEnv _UNUSED) { - // No setup necessary if ProcessResults() has been called + // No setup necessary if ProcessResults() has been called } -static void TeardownCallbackState( void ) +static void TeardownCallbackState( void ) { - // No teardown necessary if ProcessResults() has been called + // No teardown necessary if ProcessResults() has been called } -#endif // AUTO_CALLBACKS +#endif // AUTO_CALLBACKS -static OpContext *NewContext( JNIEnv *pEnv, jobject owner, - const char *callbackName, const char *callbackSig) +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; + 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) +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"); + 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); + (*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); - } - } + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + + if ( contextField != 0) + { + OpContext *pContext = (OpContext*) (long) (*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 +// 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); - } - } + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + + if ( contextField != 0) + { + OpContext *pContext = (OpContext*) (long) (*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); + 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; +#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*) (long) (*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) +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(); + 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) + 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; + 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, (long) 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) +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(); + 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) + 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; + 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, (long) 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) +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(); + 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) + 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; + //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, (long) 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) + 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; + 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*) (long) (*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, (long) 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) + 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; + 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*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField); + } + if ( recField != 0) + recRef = (DNSRecordRef) (long) (*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; + 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*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField); + } + if ( recField != 0) + recRef = (DNSRecordRef) (long) (*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; + 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, (long) pContext); + } + } + else + err = kDNSServiceErr_NoMemory; + + return err; } struct RecordRegistrationRef { - OpContext *Context; - jobject RecordObj; + OpContext *Context; + jobject RecordObj; }; -typedef struct RecordRegistrationRef RecordRegistrationRef; +typedef struct RecordRegistrationRef RecordRegistrationRef; -static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED, - DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags, - DNSServiceErrorType errorCode, void *context) +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(); + 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) +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; + 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*) (long) (*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, (long) 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) +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(); + 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) + 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; + 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, (long) 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) +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(); + 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) + 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; + 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, (long) 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) + 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]; + 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); + 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)); - } + 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); + SafeReleaseUTFChars( pEnv, serviceName, nameStr); + SafeReleaseUTFChars( pEnv, regtype, regStr); + SafeReleaseUTFChars( pEnv, domain, domStr); - return err; + 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) + jint flags, jint ifIndex, jstring fullName, + jint rrtype, jint rrclass, jbyteArray rdata) { - jbyte *pBytes; - jsize numBytes; - const char *nameStr = SafeGetUTFChars( pEnv, fullName); + jbyte *pBytes; + jsize numBytes; + const char *nameStr = SafeGetUTFChars( pEnv, fullName); - pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL); - numBytes = (*pEnv)->GetArrayLength( pEnv, rdata); + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rdata); - DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes); + DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes); - if ( pBytes != NULL) - (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0); + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0); - SafeReleaseUTFChars( pEnv, fullName, nameStr); + SafeReleaseUTFChars( pEnv, fullName, nameStr); } #define LOCAL_ONLY_NAME "loo" +#define P2P_NAME "p2p" JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED, - jint ifIndex) + jint ifIndex) { - char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE]; + char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE]; - if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly) - p = if_indextoname( ifIndex, nameBuff ); + if (ifIndex == (jint) kDNSServiceInterfaceIndexP2P) + p = P2P_NAME; + else if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly) + p = if_indextoname( ifIndex, nameBuff ); - return (*pEnv)->NewStringUTF( pEnv, p); + return (*pEnv)->NewStringUTF( pEnv, p); } JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED, - jstring ifName) + jstring ifName) { - uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly; - const char *nameStr = SafeGetUTFChars( pEnv, ifName); + uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly; + const char *nameStr = SafeGetUTFChars( pEnv, ifName); - if (strcmp(nameStr, LOCAL_ONLY_NAME)) - ifIndex = if_nametoindex( nameStr); + if (strcmp(nameStr, P2P_NAME) == 0) + ifIndex = kDNSServiceInterfaceIndexP2P; + else if (strcmp(nameStr, LOCAL_ONLY_NAME)) + ifIndex = if_nametoindex( nameStr); - SafeReleaseUTFChars( pEnv, ifName, nameStr); + SafeReleaseUTFChars( pEnv, ifName, nameStr); - return ifIndex; + return ifIndex; } #if defined(_WIN32) static char* -if_indextoname( DWORD ifIndex, char * nameBuff) +win32_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; - } + 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; - } + if (pAdapterInfo != NULL) + { + free( pAdapterInfo ); + pAdapterInfo = NULL; + } - return ifName; + return ifName; } static DWORD -if_nametoindex( const char * nameStr ) +win32_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; - } + 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; - } + if (pAdapterInfo != NULL) + { + free( pAdapterInfo ); + pAdapterInfo = NULL; + } - return ifIndex; + return ifIndex; } #endif + + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +// NOT static -- otherwise the compiler may optimize it out +// The "@(#) " pattern is a special prefix the "what" command looks for +const char VersionString_SCCS[] = "@(#) libjdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; diff --git a/usr/src/man/man1/proc.1 b/usr/src/man/man1/proc.1 index e15f756a10..3565512f0e 100644 --- a/usr/src/man/man1/proc.1 +++ b/usr/src/man/man1/proc.1 @@ -2,7 +2,7 @@ .\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved .\" Portions Copyright 2008 Chad Mynhier .\" Copyright 2012 DEY Storage Systems, Inc. All rights reserved. -.\" Copyright 2013 (c) Joyent, Inc. All rights reserved. +.\" Copyright 2016 Joyent, Inc. .\" 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] @@ -216,9 +216,10 @@ reproducible precision. Unlike \fBtime\fR(1), children of the command are not timed. .sp If the \fB-p\fR \fIpidlist\fR version is used, display a snapshot of timing -statistics for the specified processes. The \fIpidlist\fR may either be a comma -delineated list or a space delineated list. Space delineated lists must be -properly quoted to assure that they are in a single argument. +statistics for the specified processes. The \fIpidlist\fR may have a single +process or be a comma or space delineated list. If a space delineated list is +used, callers will need to ensure that it is properly quoted or escaped for +their shell. .RE .SH OPTIONS diff --git a/usr/src/man/man1m/Makefile b/usr/src/man/man1m/Makefile index 3080549c1e..8ba56e1fc1 100644 --- a/usr/src/man/man1m/Makefile +++ b/usr/src/man/man1m/Makefile @@ -13,6 +13,7 @@ # Copyright 2011, Richard Lowe # Copyright (c) 2014 Joyent, Inc. All rights reserved. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# Copyright 2016 Toomas Soome <tsoome@me.com> # include $(SRC)//Makefile.master @@ -286,6 +287,7 @@ _MANFILES= 6to4relay.1m \ makedbm.1m \ makemap.1m \ mdmonitord.1m \ + mdnsd.1m \ medstat.1m \ metaclear.1m \ metadb.1m \ diff --git a/usr/src/man/man1m/dns-sd.1m b/usr/src/man/man1m/dns-sd.1m index aeabdcc840..1b1e8082eb 100644 --- a/usr/src/man/man1m/dns-sd.1m +++ b/usr/src/man/man1m/dns-sd.1m @@ -1,335 +1,264 @@ -'\" te -.\" 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. -.\" Portions Copyright (c) 2007, Sun Microsystems, Inc. All Rights Reserved. -.TH DNS-SD 1M "Aug 21, 2007" -.SH NAME -dns-sd \- Multicast DNS (mDNS) & DNS Service Discovery (DNS-SD) Test Tool -.SH SYNOPSIS -.LP -.nf -\fBdns-sd\fR \fB-R\fR \fIname\fR \fItype\fR \fIdomain\fR \fIport\fR [\fIkey\fR=\fIvalue\fR ...] -.fi - -.LP -.nf -\fBdns-sd\fR \fB-B\fR \fItype\fR \fIdomain\fR -.fi - -.LP -.nf -\fBdns-sd\fR \fB-L\fR \fIname\fR \fItype\fR \fIdomain\fR -.fi - -.LP -.nf -\fBdns-sd\fR \fB-Q\fR \fIFQDN\fR \fIrrtype\fR \fIrrclass\fR -.fi - -.LP -.nf -\fBdns-sd\fR \fB-C\fR \fIFQDN\fR \fIrrtype\fR \fIrrclass\fR -.fi - -.LP -.nf -\fBdns-sd\fR \fB-P\fR \fIname\fR \fItype\fR \fIdomain\fR \fIport\fR \fIhost\fR \fIIP\fR [\fIkey\fR=\fIvalue\fR ...] -.fi - -.LP -.nf -\fBdns-sd\fR \fB-E\fR | \fB-F\fR | \fB-A\fR | \fB-U\fR | \fB-N\fR | \fB-T\fR | \fB-M\fR | \fB-I\fR -.fi - -.SH DESCRIPTION -.sp -.LP -The \fBdns-sd\fR command is a network diagnostic tool, much like \fBping\fR(1M) -or \fBtraceroute\fR(1M). However, unlike those tools, most of its functionality -is not implemented in the \fBdns-sd\fR executable itself, but in library code -that is available to any application. The library \fBAPI\fR that \fBdns-sd\fR -uses is documented in \fB/usr/include/dns_sd.h\fR. -.sp -.LP -The \fBdns-sd\fR command is primarily intended for interactive use. Because its -command-line arguments and output format are subject to change, invoking it -from a shell script can be unpredictable. Additionally, the asynchronous nature -of \fBDNS\fR Service Discovery does not easily lend itself to script-oriented -programming. This style of asynchronous interaction works best with -applications that are either multi-threaded, or use a main event-handling loop -to receive keystrokes, network data, and other asynchronous event notifications -as they happen. -.SH OPTIONS -.sp -.LP -The following options are supported: -.sp -.ne 2 -.na -\fB\fB-R\fR \fIname\fR \fItype\fR \fIdomain\fR \fIport\fR -[\fIkey\fR=\fIvalue\fR ...]\fR -.ad -.sp .6 -.RS 4n -Register (advertise) a service in the specified domain with the given -\fIname\fR and \fItype\fR as listening (on the current machine) on the -specified \fIport\fR. -.sp -\fIname\fR can be any arbitrary unicode text, containing any legal unicode -characters (including dots, spaces, slashes, colons, and so on without any -restrictions), up to 63 \fBUTF-8\fR bytes long. -.sp -\fItype\fR must be of the form "_app-proto._tcp" or "_app-proto._udp", where +.\" -*- 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. +.\" +.\" Copyright 2016 Toomas Soome <tsoome@me.com> +.\" +.Dd Jan 28, 2016 \" Date +.Dt DNS-SD 1M \" Document Title +.Os illumos \" Operating System +.\" +.Sh NAME +.Nm dns-sd +.Nd Multicast DNS (mDNS) & DNS Service Discovery (DNS-SD) Test Tool \" For whatis +.\" +.Sh SYNOPSIS +.Nm +.Op Fl E +.Pp +.Nm +.Op Fl F +.Pp +.Nm +.Op Fl R Ar name type domain port Op Ar key=value ... +.Pp +.Nm +.Op Fl B Ar type domain +.Pp +.Nm +.Op Fl L Ar name type domain +.Pp +.Nm +.Op Fl P Ar name type domain port host IP Op Ar key=value ... +.Pp +.Nm +.Op Fl q Ar name rrtype rrclass +.Pp +.Nm +.Op Fl Z Ar type domain +.Pp +.Nm +.Op Fl G Ns \ v4/v6/v4v6 Ar name +.Pp +.Nm +.Op Fl V +.\" +.Sh DESCRIPTION +The +.Nm +command is a network diagnostic tool, much like +.Xr ping 8 +or +.Xr traceroute 8 . +However, unlike those tools, most of its functionality is not implemented in the +.Nm +executable itself, but in library code that is available to any application. +The library API that +.Nm +uses is documented in +.Pa /usr/include/dns_sd.h . +The +.Nm +command replaces the older +mDNS +command. +.Pp +The +.Nm +command is primarily intended for interactive use. +Because its command-line arguments and output format are subject to change, +invoking it from a shell script will generally be fragile. Additionally, +the asynchronous nature of DNS Service Discovery does +not lend itself easily to script-oriented programming. For example, +calls like "browse" never complete; the action of performing a "browse" +sets in motion machinery to notify the client whenever instances of +that service type appear or disappear from the network. These +notifications continue to be delivered indefinitely, for minutes, +hours, or even days, as services come and go, until the client +explicitly terminates the call. This style of asynchronous interaction +works best with applications that are either multi-threaded, or use a +main event-handling loop to receive keystrokes, network data, and other +asynchronous event notifications as they happen. +.br +If you wish to perform DNS Service Discovery operations from a +scripting language, then the best way to do this is not to execute the +.Nm +command and then attempt to decipher the textual output, but instead to +directly call the DNS-SD APIs using a binding for your chosen language. +.br +For example, if you are programming in Ruby, then you can +directly call DNS-SD APIs using the dnssd package documented at +.Pa <http://rubyforge.org/projects/dnssd/> . +.br +Similar bindings for other languages are also in development. +.Bl -tag -width E +.It Nm Fl E +return a list of domains recommended for registering(advertising) services. +.It Nm Fl F +return a list of domains recommended for browsing services. +.Pp +Normally, on your home network, the only domain you are likely to see is "local". +However if your network administrator has created Domain Enumeration records, +then you may also see other recommended domains for registering and browsing. +.It Nm Fl R Ar name type domain port Op Ar key=value ... +register (advertise) a service in the specified +.Ar domain +with the given +.Ar name +and +.Ar type +as listening (on the current machine) on +.Ar port. +.Pp +.Ar name +can be arbitrary unicode text, containing any legal unicode characters +(including dots, spaces, slashes, colons, etc. without restriction), +up to 63 UTF-8 bytes long. +.Ar type +must be of the form "_app-proto._tcp" or "_app-proto._udp", where "app-proto" is an application protocol name registered at -http://www.dns-sd.org/ServiceTypes.html. -.sp -\fIdomain\fR is the domain in which to register the service. In current -implementations, only the local multicast domain "local" is supported. In the -future, registering will be supported in any arbitrary domain that has a -working \fBDNS\fR Update server [\fBRFC\fR 2136]. The domain "." is a synonym -for "pick a sensible default", which currently means "local". -.sp -\fIport\fR is a number from 0 to 65535, and is the \fBTCP\fR or \fBUDP\fR port -number upon which the service is listening. Registering a service on port 0 -allows an application to explicitly advertise the non-availability of a -service. -.sp +.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml . +.Pp +.Ar domain +is the domain in which to register the service. +In current implementations, only the local multicast domain "local" is +supported. In the future, registering will be supported in any arbitrary +domain that has a working DNS Update server [RFC 2136]. The +.Ar domain +"." is a synonym for "pick a sensible default" which today +means "local". +.Pp +.Ar port +is a number from 0 to 65535, and is the TCP or UDP port number upon +which the service is listening. +.Pp Additional attributes of the service may optionally be described by -\fIkey/value\fR pairs, which are stored in the advertised service's \fBDNS\fR -\fBTXT\fR record. Allowable keys and values are listed with the service -registration at http://www.dns-sd.org/ServiceTypes.html -.RE - -.sp -.ne 2 -.na -\fB\fB-B\fR \fItype\fR \fIdomain\fR\fR -.ad -.sp .6 -.RS 4n -Browse for instances of service \fItype\fR in \fIdomain\fR. -.sp -For valid types, see http://www.dns-sd.org/ServiceTypes.html. Omitting the -domain name or using "." means "pick a sensible default." -.RE - -.sp -.ne 2 -.na -\fB\fB-L\fR \fIname\fR \fItype\fR \fIdomain\fR\fR -.ad -.sp .6 -.RS 4n -Look up and display the information necessary to contact and use the named -service. This information includes the hostname of the machine where that -service is available, the port number on which the service is listening, and -(if present) \fBTXT\fR record attributes describing properties of the service. -.sp -In a typical application, browsing happens rarely, while lookup (or -"resolving") happens every time the service is used. For example, a user does -not browse the network to pick a default printer that often, but once a default -printer has been picked, that named service is resolved to its current IP -address and port number every time the user presses Cmd-P to print. -.RE - -.sp -.ne 2 -.na -\fB\fB-Q\fR \fIFQDN\fR \fIrrtype\fR \fIrrclass\fR\fR -.ad -.sp .6 -.RS 4n -Generic query for any resource record type and class. -.RE - -.sp -.ne 2 -.na -\fB\fB-C\fR \fIFQDN\fR \fIrrtype\fR \fIrrclass\fR\fR -.ad -.sp .6 -.RS 4n -Generic query for any resource record type and class. This option also -reconfirms each result from the query. Reconfirming the record instructs -\fBmdnsd\fR(1M) to verify the validity of the record. If the record is not -valid \fBmdnsd\fR(1M) flushes the record from the daemon's cache and also from -other \fBmdnsd\fR(1M) caches on the network. -.RE - -.sp -.ne 2 -.na -\fB\fB-P\fR \fIname\fR \fItype\fR \fIdomain\fR \fIport\fR \fIhost\fR \fIIP\fR -[\fIkey\fR=\fIvalue\fR ...]\fR -.ad -.sp .6 -.RS 4n -Register (advertise) a service in the specified domain with the given -\fIname\fR and \fItype\fR listening on the specified port and accessible on -another host. This option should be used to advertise by proxy a service -accessible on another host. The host name and \fBIPv4\fR address to access the -service must be specified. -.RE - -.sp -.ne 2 -.na -\fB\fB-E\fR\fR -.ad -.sp .6 -.RS 4n -Discover recommended registration domains. This option returns the recommended -domains to register a service. The recommended registration domains are -returned by querying the name servers in \fBresolv.conf\fR(4). -.RE - -.sp -.ne 2 -.na -\fB\fB-F\fR\fR -.ad -.sp .6 -.RS 4n -Discover recommended browsing domains. This option returns the recommended -domains for browsing services. The recommended browsing domains are returned by -querying the name servers in \fBresolv.conf\fR(4). -.RE - -.sp -.ne 2 -.na -\fB\fB-A\fR\fR -.ad -.sp .6 -.RS 4n -Test registering service with Multicast \fBDNS\fR and test the add, update and -delete operations of \fBDNS\fR records with Multicast \fBDNS\fR. -.RE - -.sp -.ne 2 -.na -\fB\fB-U\fR\fR -.ad -.sp .6 -.RS 4n -Test registering service with Multicast \fBDNS\fR and test updating of -\fBDNS\fR \fBTXT\fR records for a service registered with Multicast \fBDNS\fR. -.RE - -.sp -.ne 2 -.na -\fB\fB-N\fR\fR -.ad -.sp .6 -.RS 4n -Test adding a large \fBNULL\fR record for a service registered with Multicast -\fBDNS\fR. -.RE - -.sp -.ne 2 -.na -\fB\fB-T\fR\fR -.ad -.sp .6 -.RS 4n -Test adding a large \fBTXT\fR record for a service registered with Multicast -\fBDNS\fR. -.RE - -.sp -.ne 2 -.na -\fB\fB-M\fR\fR -.ad -.sp .6 -.RS 4n -Test creating a registration with multiple \fBTXT\fR records. -.RE - -.sp -.ne 2 -.na -\fB\fB-I\fR\fR -.ad -.sp .6 -.RS 4n -Test registering and then immediately updating a \fBTXT\fR record. -.RE - -.SH EXAMPLES -.LP -\fBExample 1 \fRAdvertising a printing service -.sp -.LP -The following command advertises the existence of \fBLPR\fR printing service on -port 515 on this machine, so that it will be available to \fBDNS-SD\fR -compatible printing clients: - -.sp -.in +2 -.nf -dns-sd -R "My Test" _printer._tcp. . 515 pdl=application/postscript -.fi -.in -2 -.sp - -.sp -.LP -For this registration to be useful, the LPR service should be available on port -515. Advertising a service that does not exist is not very useful. -.LP -\fBExample 2 \fRAdvertising a web page -.sp -.LP -The following command advertises a web page being served by an \fBHTTP\fR -server on port 80 on this machine, so that it will appear on the Bonjour list -in Safari and other DNS-SD compatible Web clients: - -.sp -.in +2 -.nf -dns-sd -R "My Test" _http._tcp . 80 path=/path-to-page.html -.fi -.in -2 -.sp - -.LP -\fBExample 3 \fRFinding the advertised web pages on the local network -.sp -.LP -The following command finds the advertised web pages on the local network (the -same list that Safari shows): - -.sp -.in +2 -.nf -dns-sd -B _http._tcp -.fi -.in -2 -.sp - -.SH ATTRIBUTES -.sp -.LP -See \fBattributes\fR(5) for descriptions of the following attributes: -.sp - -.sp -.TS -box; -c | c -l | l . -ATTRIBUTE TYPE ATTRIBUTE VALUE -_ -Interface Stability Volatile -.TE - -.SH SEE ALSO -.sp -.LP -\fBmdnsd\fR(1M), \fBping\fR(1M), \fBtraceroute\fR(1M), \fBresolv.conf\fR(4), -\fBattributes\fR(5) +key/value pairs, which are stored in the advertised service's DNS TXT +record. Allowable keys and values are listed with the service +registration at +.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml . +.It Nm Fl B Ar type domain +browse for instances of service +.Ar type +in +.Ar domain . +.Pp +For valid +.Ar type Ns s +see +.Pa http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml . +as described above. Omitting the +.Ar domain +or using "." means "pick a sensible default." +.It Nm Fl L Ar name type domain +look up and display the information necessary to contact and use the +named service: the hostname of the machine where that service is +available, the port number on which the service is listening, and (if +present) TXT record attributes describing properties of the service. +.Pp +Note that in a typical application, browsing may only happen rarely, while lookup +(or "resolving") happens every time the service is used. For example, a +user browses the network to pick a default printer fairly rarely, but once +a default printer has been picked, that named service is resolved to its +current IP address and port number every time the user presses Cmd-P to +print. +.It Nm Fl P Ar name type domain port host IP Op Ar key=value ... +create a proxy advertisement for a service running on(offered by) some other machine. +The two new options are Host, a name for the device and IP, the address of it. +.Pp +The service for which you create a proxy advertisement does not necessarily have to be on your local network. +You can set up a local proxy for a website on the Internet. +.It Nm Fl q Ar name rrtype rrclass +look up any DNS name, resource record type, and resource record class, +not necessarily DNS-SD names and record types. +If rrtype is not specified, it queries for the IPv4 address of the name, +if rrclass is not specified, IN class is assumed. If the name is not a fully +qualified domain name, then search domains may be appended. +.It Nm Fl Z Ar type domain +browse for service instances and display output in zone file format. +.It Nm Fl G Ns \ v4/v6/v4v6 Ar name +look up the IP address information of the name. +If v4 is specified, the IPv4 address of the name is looked up, +if v6 is specified the IPv6 address is looked up. If v4v6 is specified both the IPv4 and IPv6 +address is looked up. If the name is not a fully qualified domain name, +then search domains may be appended. +.It Nm Fl V +return the version of the currently running daemon/system service. +.El +.Sh FILES +.Pa /usr/bin/dns-sd \" Pathname +.\" +.Sh EXAMPLES +To advertise the existence of LPR printing service on port 515 on this +machine, such that it will be discovered by the Mac OS X printing software +and other DNS-SD compatible printing clients, use: +.Pp +.Dl Nm Fl R Ns \ \&"My Test\&" _printer._tcp. \&. 515 pdl=application/postscript +.Pp +For this registration to be useful, you need to actually have LPR service +available on port 515. Advertising a service that does not exist is not +very useful, and will be confusing and annoying to other people on the +network. +.Pp +Similarly, to advertise a web page being served by an HTTP +server on port 80 on this machine, such that it will show up in the +Bonjour list in Safari and other DNS-SD compatible Web clients, use: +.Pp +.Dl Nm Fl R Ns \ \&"My Test\&" _http._tcp \&. 80 path=/path-to-page.html +.Pp +To find the advertised web pages on the local network (the same list that +Safari shows), use: +.Pp +.Dl Nm Fl B Ns \ _http._tcp +.Pp +While that command is running, in another window, try the +.Nm Fl R +example given above to advertise a web page, and you should see the +"Add" event reported to the +.Nm Fl B +window. Now press Ctrl-C in the +.Nm Fl R +window and you should see the "Remove" event reported to the +.Nm Fl B +window. +.Pp +In the example below, the www.apple.com web page is advertised as a service called "apple", +running on a target host called apple.local, which resolves to 17.149.160.49. +.Pp +.Dl Nm Fl P Ns \ apple _http._tcp \&"\&"\& 80 apple.local 17.149.160.49 +.Pp +The Bonjour menu in the Safari web browser will now show "apple". +The same IP address can be reached by entering apple.local in the web browser. +In either case, the request will be resolved to the IP address and browser will show +contents associated with www.apple.com. +.Pp +If a client wants to be notified of changes in server state, it can +initiate a query for the service's particular record and leave it running. +For example, to monitor the status of an iChat user you can use: +.Pp +.Dl Nm Fl q Ns \ someone@ex1._presence._tcp.local txt +.Pp +Everytime status of that user(someone) changes, you will see a new TXT record result reported. +.Pp +You can also query for a unicast name like www.apple.com and monitor its status. +.Pp +.Dl Nm Fl q Ns \ www.apple.com +.Sh INTERFACE STABILITY +.Sy Volatile . +.Sh SEE ALSO +.Xr mdnsd 1M , +.Xr ping 1M , +.Xr traceroute 1M , +.Xr resolv.conf 4 diff --git a/usr/src/man/man1m/mdnsd.1m b/usr/src/man/man1m/mdnsd.1m new file mode 100644 index 0000000000..73d51e523c --- /dev/null +++ b/usr/src/man/man1m/mdnsd.1m @@ -0,0 +1,108 @@ +.\" -*- 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. +.\" +.\" Copyright 2016 Toomas Soome <tsoome@me.com> +.\" +.Dd Jan 28, 2016 \" Date +.Dt MDNSD 1M \" Document Title +.Os illumos \" Operating System +.\" +.Sh NAME +.Nm mdnsd +.Nd Multicast and Unicast DNS daemon \" Name Description for whatis database +.\" +.Sh SYNOPSIS +.Nm +.\" +.Sh DESCRIPTION +.Nm +(also known as +.Nm mDNSResponder +on some systems) +is a daemon invoked at boot time to implement Multicast DNS and DNS Service Discovery. +.Pp +.Nm +listens on UDP port 5353 for Multicast DNS Query packets. +When it receives a query for which it knows an answer, +.Nm +issues the appropriate Multicast DNS Reply packet. +.Pp +.Nm +also performs Unicast and Multicast DNS Queries on behalf of client processes, and +maintains a cache of the replies. +.Pp +.Nm +has no user-specifiable command-line argument, and users should not run +.Nm +manually. +.Sh LOGGING +There are several methods with which to examine +.Nm Ns 's internal state for debugging and diagnostic purposes. The syslog(3C) +logging levels map as follows: +.Pp +.Dl Error - Error messages +.Dl Warning - Client-initiated operations +.Dl Notice - Sleep proxy operations +.Dl Info - Informational messages +.Pp +By default, only log level Error is logged. +.Pp +A SIGUSR1 signal toggles additional logging, with Warning and Notice +enabled by default: +.Pp +.Dl % sudo pkill -USR1 mdnsd +.Pp +A SIGUSR2 signal toggles packet logging: +.Pp +.Dl % sudo pkill -USR2 mdnsd +.Pp +A SIGINFO signal will dump a snapshot summary of the internal state: +.Pp +.Dl % sudo pkill -INFO mdnsd +.Sh FILES +.Pa /usr/lib/inet/mdnsd \" Pathname +.\" +.Sh INFO +For information on Multicast DNS, see +.Pa http://www.multicastdns.org/ +.Pp +For information on DNS Service Discovery, see +.Pa http://www.dns-sd.org/ +.Pp +For information on how to use the Multicast DNS and the +DNS Service Discovery APIs on Mac OS X and other platforms, see +.Pa http://developer.apple.com/bonjour/ +.Pp +For the source code to +.Nm , see +.Pa http://developer.apple.com/darwin/projects/bonjour/ +.\" +.Sh INTERFACE STABILITY +.Sy Volatile . +.Sh SEE ALSO +.Xr dns-sd 1M +.\" +.Sh NOTES +The +.Nm +service is managed by the service management facility, +\fBsmf\fR(5), under the service identifier: +.sp +.Dl svc:/network/dns/multicast:default +.sp +Administrative actions on this service, such as enabling, disabling, or +requesting restart, can be performed using \fBsvcadm\fR(1M). The service's +status can be queried using the \fBsvcs\fR(1) command. diff --git a/usr/src/pkg/manifests/service-network-dns-mdns.mf b/usr/src/pkg/manifests/service-network-dns-mdns.mf index 89deca5b22..d88be620ac 100644 --- a/usr/src/pkg/manifests/service-network-dns-mdns.mf +++ b/usr/src/pkg/manifests/service-network-dns-mdns.mf @@ -22,6 +22,7 @@ # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2012 Nexenta Systems, Inc. All rights reserved. +# Copyright 2016 Toomas Soome <tsoome@me.com> # set name=pkg.fmri value=pkg:/service/network/dns/mdns@$(PKGVERS) @@ -145,6 +146,7 @@ file path=usr/share/lib/java/javadoc/dnssd/api/stylesheet.css group=other file path=usr/share/lib/java/javadoc/dnssd/examples/BrowserApp.jar group=sys file path=usr/share/lib/java/javadoc/dnssd/examples/SimpleChat.jar group=sys file path=usr/share/man/man1m/dns-sd.1m +file path=usr/share/man/man1m/mdnsd.1m file path=usr/share/man/man3dns_sd/DNSServiceBrowse.3dns_sd file path=usr/share/man/man3dns_sd/DNSServiceConstructFullName.3dns_sd file path=usr/share/man/man3dns_sd/DNSServiceCreateConnection.3dns_sd |