diff options
author | rs200217 <none@none> | 2007-08-20 11:44:22 -0700 |
---|---|---|
committer | rs200217 <none@none> | 2007-08-20 11:44:22 -0700 |
commit | 4b22b9337f359bfd063322244f5336cc7c6ffcfa (patch) | |
tree | d935e6adb14cc353974e0fcf2dc89a47f18b9971 /usr | |
parent | d96018ff0218b4466f62ea165b3dfd64ef271965 (diff) | |
download | illumos-gate-4b22b9337f359bfd063322244f5336cc7c6ffcfa.tar.gz |
PSARC 2005/562 Multicast DNS and Service Discovery
6550311 Multicast DNS and service discovery support
Diffstat (limited to 'usr')
118 files changed, 40345 insertions, 71 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 14950a9cb5..036049287c 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -818,6 +818,7 @@ MANIFEST_SUBDIRS= \ cmd-inet/usr.lib/in.ripngd \ cmd-inet/usr.lib/in.timed \ cmd-inet/usr.lib/inetd \ + cmd-inet/usr.lib/mdnsd \ cmd-inet/usr.lib/slpd \ cmd-inet/usr.lib/wpad \ cmd-inet/usr.sbin \ diff --git a/usr/src/cmd/Makefile.cmd b/usr/src/cmd/Makefile.cmd index 6a3cccfdb6..89d085da4d 100644 --- a/usr/src/cmd/Makefile.cmd +++ b/usr/src/cmd/Makefile.cmd @@ -236,6 +236,7 @@ ROOTSVCSYSTEMDEVICE= $(ROOTSVCSYSTEM)/device ROOTSVCSYSTEMFILESYSTEM= $(ROOTSVCSYSTEM)/filesystem ROOTSVCSYSTEMSECURITY= $(ROOTSVCSYSTEM)/security ROOTSVCNETWORK= $(ROOTVARSVCMANIFEST)/network +ROOTSVCNETWORKDNS= $(ROOTSVCNETWORK)/dns ROOTSVCNETWORKLDAP= $(ROOTSVCNETWORK)/ldap ROOTSVCNETWORKNFS= $(ROOTSVCNETWORK)/nfs ROOTSVCNETWORKNIS= $(ROOTSVCNETWORK)/nis diff --git a/usr/src/cmd/cmd-inet/etc/services b/usr/src/cmd/cmd-inet/etc/services index c5a9e591ba..a39fbf5427 100644 --- a/usr/src/cmd/cmd-inet/etc/services +++ b/usr/src/cmd/cmd-inet/etc/services @@ -136,5 +136,7 @@ nfsd 2049/tcp nfs # NFS server daemon (cots) eklogin 2105/tcp # Kerberos encrypted rlogin lockd 4045/udp # NFS lock daemon/manager lockd 4045/tcp +mdns 5353/udp # Multicast DNS +mdns 5353/tcp dtspc 6112/tcp # CDE subprocess control fs 7100/tcp # Font server diff --git a/usr/src/cmd/cmd-inet/usr.bin/Makefile b/usr/src/cmd/cmd-inet/usr.bin/Makefile index 1f418ae70a..4a120cc7f3 100644 --- a/usr/src/cmd/cmd-inet/usr.bin/Makefile +++ b/usr/src/cmd/cmd-inet/usr.bin/Makefile @@ -19,13 +19,13 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # -PROG= finger rdate ruptime rwho whois +PROG= dns-sd finger rdate ruptime rwho whois SUIDPROG= rcp rlogin rsh ALL= $(PROG) $(SUIDPROG) SRCS= $(ALL:%=%.c) @@ -72,10 +72,12 @@ CPPFLAGS += -DSYSV -DSTRNET -DBSD_COMP -I$(CMDINETCOMMONDIR) # PROGS are lint clean. $(LINTCLEAN) := CFLAGS += $(CCVERBOSE) +dns-sd := CFLAGS += $(C99_ENABLE) finger := CFLAGS += $(CCVERBOSE) # Enable large file support for reading the lastlog file. finger := CPPFLAGS += -D_FILE_OFFSET_BITS=64 +dns-sd := LDLIBS += -lsocket -ldns_sd finger := LDLIBS += -lnsl -lcurses -lsocket rcp lint-rcp := LDLIBS += -lsocket -lsec -lsendfile rdate lint-rdate:= LDLIBS += -lsocket diff --git a/usr/src/cmd/cmd-inet/usr.bin/dns-sd.c b/usr/src/cmd/cmd-inet/usr.bin/dns-sd.c new file mode 100644 index 0000000000..aa123c921c --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.bin/dns-sd.c @@ -0,0 +1,805 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Formatting notes: + * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion + * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>, + * but for the sake of brevity here I will say just this: Curly braces are not syntactially + * part of an "if" statement; they are the beginning and ending markers of a compound statement; + * therefore common sense dictates that if they are part of a compound statement then they + * should be indented to the same level as everything else in that compound statement. + * Indenting curly braces at the same level as the "if" implies that curly braces are + * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" + * thinking that variables x and y are both of type "char*" -- and anyone who doesn't + * understand why variable y is not of type "char*" just proves the point that poor code + * layout leads people to unfortunate misunderstandings about how the C language really works.) + +To build this tool, copy and paste the following into a command line: + +OS X: +gcc dns-sd.c -o dns-sd + +POSIX systems: +gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd + +Windows: +cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib +(may require that you run a Visual Studio script such as vsvars32.bat first) +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +// For testing changes to dnssd_clientstub.c, uncomment this line and the #include below +// #define __APPLE_API_PRIVATE 1 + +#include "dns_sd.h" +#include <ctype.h> +#include <stdio.h> // For stdout, stderr +#include <stdlib.h> // For exit() +#include <string.h> // For strlen(), strcpy(), bzero() +#include <errno.h> // For errno, EINTR +#include <time.h> +#include <sys/types.h> // For u_char + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#include <process.h> +typedef int pid_t; +#define getpid _getpid +#define strcasecmp _stricmp +#define snprintf _snprintf +static const char kFilePathSep = '\\'; +#else +#include <unistd.h> // For getopt() and optind +#include <netdb.h> // For getaddrinfo() +#include <sys/time.h> // For struct timeval +#include <sys/socket.h> // For AF_INET +#include <netinet/in.h> // For struct sockaddr_in() +#include <arpa/inet.h> // For inet_addr() +static const char kFilePathSep = '/'; +#endif + +//#include "../mDNSShared/dnssd_clientstub.c" + +//************************************************************************************************************* +// Globals + +typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; + +static int operation; +static uint32_t opinterface = kDNSServiceInterfaceIndexAny; +static DNSServiceRef client = NULL; +static DNSServiceRef client2 = NULL; +static int num_printed; +static char addtest = 0; +static DNSRecordRef record = NULL; +static char myhinfoW[14] = "\002PC\012Windows XP"; +static char myhinfoX[ 9] = "\003Mac\004OS X"; +static char updatetest[3] = "\002AA"; +static char bigNULL[8200]; + +// Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this +#define LONG_TIME 100000000 + +static volatile int stopNow = 0; +static volatile int timeOut = LONG_TIME; + +//************************************************************************************************************* +// Supporting Utility Function + +static uint16_t GetRRType(const char *s) + { + if (!strcasecmp(s, "A" )) return(kDNSServiceType_A); + else if (!strcasecmp(s, "NS" )) return(kDNSServiceType_NS); + else if (!strcasecmp(s, "MD" )) return(kDNSServiceType_MD); + else if (!strcasecmp(s, "MF" )) return(kDNSServiceType_MF); + else if (!strcasecmp(s, "CNAME" )) return(kDNSServiceType_CNAME); + else if (!strcasecmp(s, "SOA" )) return(kDNSServiceType_SOA); + else if (!strcasecmp(s, "MB" )) return(kDNSServiceType_MB); + else if (!strcasecmp(s, "MG" )) return(kDNSServiceType_MG); + else if (!strcasecmp(s, "MR" )) return(kDNSServiceType_MR); + else if (!strcasecmp(s, "NULL" )) return(kDNSServiceType_NULL); + else if (!strcasecmp(s, "WKS" )) return(kDNSServiceType_WKS); + else if (!strcasecmp(s, "PTR" )) return(kDNSServiceType_PTR); + else if (!strcasecmp(s, "HINFO" )) return(kDNSServiceType_HINFO); + else if (!strcasecmp(s, "MINFO" )) return(kDNSServiceType_MINFO); + else if (!strcasecmp(s, "MX" )) return(kDNSServiceType_MX); + else if (!strcasecmp(s, "TXT" )) return(kDNSServiceType_TXT); + else if (!strcasecmp(s, "RP" )) return(kDNSServiceType_RP); + else if (!strcasecmp(s, "AFSDB" )) return(kDNSServiceType_AFSDB); + else if (!strcasecmp(s, "X25" )) return(kDNSServiceType_X25); + else if (!strcasecmp(s, "ISDN" )) return(kDNSServiceType_ISDN); + else if (!strcasecmp(s, "RT" )) return(kDNSServiceType_RT); + else if (!strcasecmp(s, "NSAP" )) return(kDNSServiceType_NSAP); + else if (!strcasecmp(s, "NSAP_PTR")) return(kDNSServiceType_NSAP_PTR); + else if (!strcasecmp(s, "SIG" )) return(kDNSServiceType_SIG); + else if (!strcasecmp(s, "KEY" )) return(kDNSServiceType_KEY); + else if (!strcasecmp(s, "PX" )) return(kDNSServiceType_PX); + else if (!strcasecmp(s, "GPOS" )) return(kDNSServiceType_GPOS); + else if (!strcasecmp(s, "AAAA" )) return(kDNSServiceType_AAAA); + else if (!strcasecmp(s, "LOC" )) return(kDNSServiceType_LOC); + else if (!strcasecmp(s, "NXT" )) return(kDNSServiceType_NXT); + else if (!strcasecmp(s, "EID" )) return(kDNSServiceType_EID); + else if (!strcasecmp(s, "NIMLOC" )) return(kDNSServiceType_NIMLOC); + else if (!strcasecmp(s, "SRV" )) return(kDNSServiceType_SRV); + else if (!strcasecmp(s, "ATMA" )) return(kDNSServiceType_ATMA); + else if (!strcasecmp(s, "NAPTR" )) return(kDNSServiceType_NAPTR); + else if (!strcasecmp(s, "KX" )) return(kDNSServiceType_KX); + else if (!strcasecmp(s, "CERT" )) return(kDNSServiceType_CERT); + else if (!strcasecmp(s, "A6" )) return(kDNSServiceType_A6); + else if (!strcasecmp(s, "DNAME" )) return(kDNSServiceType_DNAME); + else if (!strcasecmp(s, "SINK" )) return(kDNSServiceType_SINK); + else if (!strcasecmp(s, "OPT" )) return(kDNSServiceType_OPT); + else if (!strcasecmp(s, "TKEY" )) return(kDNSServiceType_TKEY); + else if (!strcasecmp(s, "TSIG" )) return(kDNSServiceType_TSIG); + else if (!strcasecmp(s, "IXFR" )) return(kDNSServiceType_IXFR); + else if (!strcasecmp(s, "AXFR" )) return(kDNSServiceType_AXFR); + else if (!strcasecmp(s, "MAILB" )) return(kDNSServiceType_MAILB); + else if (!strcasecmp(s, "MAILA" )) return(kDNSServiceType_MAILA); + else if (!strcasecmp(s, "ANY" )) return(kDNSServiceType_ANY); + else return(atoi(s)); + } + +//************************************************************************************************************* +// Sample callback functions for each of the operation types + +static void printtimestamp(void) + { + struct tm tm; + int ms; +#ifdef _WIN32 + SYSTEMTIME sysTime; + time_t uct = time(NULL); + tm = *localtime(&uct); + GetLocalTime(&sysTime); + ms = sysTime.wMilliseconds; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + localtime_r((time_t*)&tv.tv_sec, &tm); + ms = tv.tv_usec/1000; +#endif + printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); + } + +#define DomainMsg(X) (((X) & kDNSServiceFlagsDefault) ? "(Default)" : \ + ((X) & kDNSServiceFlagsAdd) ? "Added" : "Removed") + +static const char *GetNextLabel(const char *cstr, char label[64]) + { + char *ptr = label; + while (*cstr && *cstr != '.') // While we have characters in the label... + { + char c = *cstr++; + if (c == '\\') + { + c = *cstr++; + if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1])) + { + int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal + int v1 = cstr[ 0] - '0'; + int v2 = cstr[ 1] - '0'; + int val = v0 * 100 + v1 * 10 + v2; + if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it + } + } + *ptr++ = c; + if (ptr >= label+64) return(NULL); + } + if (*cstr) cstr++; // Skip over the trailing dot (if present) + *ptr++ = 0; + return(cstr); + } + +static void DNSSD_API enum_reply(DNSServiceRef client, const DNSServiceFlags flags, uint32_t ifIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) + { + DNSServiceFlags partialflags = flags & ~(kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault); + int labels = 0, depth = 0, i, initial = 0; + char text[64]; + const char *label[128]; + + (void)client; // Unused + (void)ifIndex; // Unused + (void)context; // Unused + + // 1. Print the header + if (num_printed++ == 0) printf("Timestamp Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing"); + printtimestamp(); + if (errorCode) + printf("Error code %d\n", errorCode); + else if (!*replyDomain) + printf("Error: No reply domain\n"); + else + { + printf("%-10s", DomainMsg(flags)); + printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : ""); + if (partialflags) printf("Flags: %4X ", partialflags); + else printf(" "); + + // 2. Count the labels + while (*replyDomain) + { + label[labels++] = replyDomain; + replyDomain = GetNextLabel(replyDomain, text); + } + + // 3. Decide if we're going to clump the last two or three labels (e.g. "apple.com", or "nicta.com.au") + if (labels >= 3 && replyDomain - label[labels-1] <= 3 && label[labels-1] - label[labels-2] <= 4) initial = 3; + else if (labels >= 2 && replyDomain - label[labels-1] <= 4) initial = 2; + else initial = 1; + labels -= initial; + + // 4. Print the initial one-, two- or three-label clump + for (i=0; i<initial; i++) + { + GetNextLabel(label[labels+i], text); + if (i>0) printf("."); + printf("%s", text); + } + printf("\n"); + + // 5. Print the remainder of the hierarchy + for (depth=0; depth<labels; depth++) + { + printf(" "); + for (i=0; i<=depth; i++) printf("- "); + GetNextLabel(label[labels-1-depth], text); + printf("> %s\n", text); + } + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } + +static void DNSSD_API browse_reply(DNSServiceRef client, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, + const char *replyName, const char *replyType, const char *replyDomain, void *context) + { + char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + (void)client; // Unused + (void)context; // Unused + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-25s %s\n", "Domain", "Service Type", "Instance Name"); + printtimestamp(); + if (errorCode) printf("Error code %d\n", errorCode); + else printf("%s%6X%3d %-25s %-25s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName); + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } + +static void ShowTXTRecord(uint16_t txtLen, const unsigned char *txtRecord) + { + const unsigned char *ptr = txtRecord; + const unsigned char *max = txtRecord + txtLen; + while (ptr < max) + { + const unsigned char *const end = ptr + 1 + ptr[0]; + if (end > max) { printf("<< invalid data >>"); break; } + if (++ptr < end) printf(" "); // As long as string is non-empty, begin with a space + while (ptr<end) + { + // We'd like the output to be shell-friendly, so that it can be copied and pasted unchanged into a "dns-sd -R" command. + // However, this is trickier than it seems. Enclosing a string in double quotes doesn't necessarily make it + // shell-safe, because shells still expand variables like $foo even when they appear inside quoted strings. + // Enclosing a string in single quotes is better, but when using single quotes even backslash escapes are ignored, + // meaning there's simply no way to represent a single quote (or apostrophe) inside a single-quoted string. + // The only remaining solution is not to surround the string with quotes at all, but instead to use backslash + // escapes to encode spaces and all other known shell metacharacters. + // (If we've missed any known shell metacharacters, please let us know.) + // In addition, non-printing ascii codes (0-31) are displayed as \xHH, using a two-digit hex value. + // Because '\' is itself a shell metacharacter (the shell escape character), it has to be escaped as "\\" to survive + // the round-trip to the shell and back. This means that a single '\' is represented here as EIGHT backslashes: + // The C compiler eats half of them, resulting in four appearing in the output. + // The shell parses those four as a pair of "\\" sequences, passing two backslashes to the "dns-sd -R" command. + // The "dns-sd -R" command interprets this single "\\" pair as an escaped literal backslash. Sigh. + if (strchr(" &;`'\"|*?~<>^()[]{}$", *ptr)) printf("\\"); + if (*ptr == '\\') printf("\\\\\\\\"); + else if (*ptr >= ' ' ) printf("%c", *ptr); + else printf("\\\\x%02X", *ptr); + ptr++; + } + } + } + +static void DNSSD_API resolve_reply(DNSServiceRef client, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, + const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txtRecord, void *context) + { + union { uint16_t s; u_char b[2]; } port = { opaqueport }; + uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; + + (void)client; // Unused + (void)ifIndex; // Unused + (void)context; // Unused + + printtimestamp(); + if (errorCode) printf("Error code %d\n", errorCode); + else + { + printf("%s can be reached at %s:%u", fullname, hosttarget, PortAsNumber); + if (flags) printf(" Flags: %X", flags); + // Don't show degenerate TXT records containing nothing but a single empty string + if (txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); } + printf("\n"); + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } + +static void myTimerCallBack(void) + { + DNSServiceErrorType err = kDNSServiceErr_Unknown; + + switch (operation) + { + case 'A': + { + switch (addtest) + { + case 0: printf("Adding Test HINFO record\n"); + err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_HINFO, sizeof(myhinfoW), &myhinfoW[0], 0); + addtest = 1; + break; + case 1: printf("Updating Test HINFO record\n"); + err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 0); + addtest = 2; + break; + case 2: printf("Removing Test HINFO record\n"); + err = DNSServiceRemoveRecord(client, record, 0); + addtest = 0; + break; + } + } + break; + + case 'U': + { + if (updatetest[1] != 'Z') updatetest[1]++; + else updatetest[1] = 'A'; + updatetest[0] = 3 - updatetest[0]; + updatetest[2] = updatetest[1]; + printf("Updating Test TXT record to %c\n", updatetest[1]); + err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 0); + } + break; + + case 'N': + { + printf("Adding big NULL record\n"); + err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0); + timeOut = LONG_TIME; + } + break; + } + + if (err != kDNSServiceErr_NoError) + { + fprintf(stderr, "DNSService call failed %ld\n", (long int)err); + stopNow = 1; + } + } + +static void DNSSD_API reg_reply(DNSServiceRef client, const DNSServiceFlags flags, DNSServiceErrorType errorCode, + const char *name, const char *regtype, const char *domain, void *context) + { + (void)client; // Unused + (void)flags; // Unused + (void)context; // Unused + + printf("Got a reply for %s.%s%s: ", name, regtype, domain); + + if (errorCode == kDNSServiceErr_NoError) + { + printf("Name now registered and active\n"); + if (operation == 'A' || operation == 'U' || operation == 'N') timeOut = 5; + } + else if (errorCode == kDNSServiceErr_NameConflict) + { + printf("Name in use, please choose another\n"); + exit(-1); + } + else + printf("Error %d\n", errorCode); + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } + +static void DNSSD_API qr_reply(DNSServiceRef sdRef, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, + const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) + { + char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + const unsigned char *rd = rdata; + const unsigned char *end = (const unsigned char *) rdata + rdlen; + char rdb[1000]; + int unknowntype = 0; + + (void)sdRef; // Unused + (void)flags; // Unused + (void)ifIndex; // Unused + (void)ttl; // Unused + (void)context; // Unused + + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); + printtimestamp(); + if (errorCode) + printf("Error code %d\n", errorCode); + else + { + switch (rrtype) + { + case kDNSServiceType_A: sprintf(rdb, "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); break; + case kDNSServiceType_AAAA: sprintf(rdb, "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], + rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); break; + break; + default : sprintf(rdb, "%d bytes%s", rdlen, rdlen ? ":" : ""); unknowntype = 1; break; + } + + printf("%s%6X%3d %-30s%4d%4d %s", op, flags, ifIndex, fullname, rrtype, rrclass, rdb); + if (unknowntype) while (rd < end) printf(" %02X", *rd++); + printf("\n"); + + if (operation == 'C') + if (flags & kDNSServiceFlagsAdd) + DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata); + } + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } + +//************************************************************************************************************* +// The main test function + +static void HandleEvents(void) + { + int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1; + int dns_sd_fd2 = client2 ? DNSServiceRefSockFD(client2) : -1; + int nfds = dns_sd_fd + 1; + fd_set readfds; + struct timeval tv; + int result; + + if (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1; + + while (!stopNow) + { + // 1. Set up the fd_set as usual here. + // This example client has no file descriptors of its own, + // but a real application would call FD_SET to add them to the set here + FD_ZERO(&readfds); + + // 2. Add the fd for our client(s) to the fd_set + if (client ) FD_SET(dns_sd_fd , &readfds); + if (client2) FD_SET(dns_sd_fd2, &readfds); + + // 3. Set up the timeout. + tv.tv_sec = timeOut; + tv.tv_usec = 0; + + result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (result > 0) + { + DNSServiceErrorType err = kDNSServiceErr_NoError; + if (client && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(client ); + else if (client2 && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client2); + if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } + } + else if (result == 0) + myTimerCallBack(); + else + { + printf("select() returned %d errno %d %s\n", result, errno, strerror(errno)); + if (errno != EINTR) stopNow = 1; + } + } + } + +static int getfirstoption( int argc, char **argv, const char *optstr, int *pOptInd) +// Return the recognized option in optstr and the option index of the next arg. +#if NOT_HAVE_GETOPT + { + int i; + for ( i=1; i < argc; i++) + { + if ( argv[i][0] == '-' && &argv[i][1] && + NULL != strchr( optstr, argv[i][1])) + { + *pOptInd = i + 1; + return argv[i][1]; + } + } + return -1; + } +#else + { + int operation = getopt(argc, (char * const *)argv, optstr); + *pOptInd = optind; + return operation; + } +#endif + +static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef record, const DNSServiceFlags flags, + DNSServiceErrorType errorCode, void * context) + { + char *name = (char *)context; + + (void)service; // Unused + (void)record; // Unused + (void)flags; // Unused + + printf("Got a reply for %s: ", name); + switch (errorCode) + { + case kDNSServiceErr_NoError: printf("Name now registered and active\n"); break; + case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); exit(-1); + default: printf("Error %d\n", errorCode); break; + } + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } + +static unsigned long getip(const char *const name) + { + unsigned long ip = 0; + struct addrinfo hints; + struct addrinfo * addrs = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + + if (getaddrinfo(name, NULL, &hints, &addrs) == 0) + { + ip = ((struct sockaddr_in*) addrs->ai_addr)->sin_addr.s_addr; + } + + if (addrs) + { + freeaddrinfo(addrs); + } + + return(ip); + } + +static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef *sdRef, const char *host, const char *ip) + { + // Call getip() after the call DNSServiceCreateConnection(). + // On the Win32 platform, WinSock must be initialized for getip() to succeed. + // Any DNSService* call will initialize WinSock for us, so we make sure + // DNSServiceCreateConnection() is called before getip() is. + unsigned long addr = 0; + DNSServiceErrorType err = DNSServiceCreateConnection(sdRef); + if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } + addr = getip(ip); + return(DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, kDNSServiceInterfaceIndexAny, host, + kDNSServiceType_A, kDNSServiceClass_IN, sizeof(addr), &addr, 240, MyRegisterRecordCallback, (void*)host)); + // Note, should probably add support for creating proxy AAAA records too, one day + } + +#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ + ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ + ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : 0) + +#define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1])) + +static DNSServiceErrorType RegisterService(DNSServiceRef *sdRef, + const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv) + { + uint16_t PortAsNumber = atoi(port); + Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; + unsigned char txt[2048] = ""; + unsigned char *ptr = txt; + int i; + + if (nam[0] == '.' && nam[1] == 0) nam = ""; // We allow '.' on the command line as a synonym for empty string + if (dom[0] == '.' && dom[1] == 0) dom = ""; // We allow '.' on the command line as a synonym for empty string + + printf("Registering Service %s.%s%s%s", nam[0] ? nam : "<<Default>>", typ, dom[0] ? "." : "", dom); + if (host && *host) printf(" host %s", host); + printf(" port %s\n", port); + + if (argc) + { + for (i = 0; i < argc; i++) + { + const char *p = argv[i]; + *ptr = 0; + while (*p && *ptr < 255 && ptr + 1 + *ptr < txt+sizeof(txt)) + { + if (p[0] != '\\' || p[1] == 0) { ptr[++*ptr] = *p; p+=1; } + else if (p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3])) { ptr[++*ptr] = HexPair(p+2); p+=4; } + else { ptr[++*ptr] = p[1]; p+=2; } + } + ptr += 1 + *ptr; + } + ShowTXTRecord(ptr-txt, txt); + printf("\n"); + } + + return(DNSServiceRegister(sdRef, /* kDNSServiceFlagsAllowRemoteQuery */ 0, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL)); + } + +int main(int argc, char **argv) + { + DNSServiceErrorType err; + char *dom; + int optind; + + // Extract the program name from argv[0], which by convention contains the path to this executable. + // Note that this is just a voluntary convention, not enforced by the kernel -- + // the process calling exec() can pass bogus data in argv[0] if it chooses to. + const char *a0 = strrchr(argv[0], kFilePathSep) + 1; + if (a0 == (const char *)1) a0 = argv[0]; + + if (argc > 1 && !strcmp(argv[1], "-lo")) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexLocalOnly; + printf("Using LocalOnly\n"); + } + + if (argc > 2 && !strcmp(argv[1], "-i") && atoi(argv[2])) + { + opinterface = atoi(argv[2]); + argc -= 2; + argv += 2; + printf("Using interface %d\n", opinterface); + } + + if (argc < 2) goto Fail; // Minimum command line is the command name and one argument + operation = getfirstoption( argc, argv, "EFBLRPQCAUNTMI", &optind); + if (operation == -1) goto Fail; + + switch (operation) + { + case 'E': printf("Looking for recommended registration domains:\n"); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, opinterface, enum_reply, NULL); + break; + + case 'F': printf("Looking for recommended browsing domains:\n"); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, opinterface, enum_reply, NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "bonjour.nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "ibm.com.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL); + break; + + case 'B': if (argc < optind+1) goto Fail; + dom = (argc < optind+2) ? "" : argv[optind+1]; + if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string + printf("Browsing for %s%s%s\n", argv[optind+0], dom[0] ? "." : "", dom); + err = DNSServiceBrowse(&client, 0, opinterface, argv[optind+0], dom, browse_reply, NULL); + break; + + case 'L': if (argc < optind+2) goto Fail; + dom = (argc < optind+3) ? "local" : argv[optind+2]; + if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" + printf("Lookup %s.%s.%s\n", argv[optind+0], argv[optind+1], dom); + err = DNSServiceResolve(&client, 0, opinterface, argv[optind+0], argv[optind+1], dom, (DNSServiceResolveReply)resolve_reply, NULL); + break; + + case 'R': if (argc < optind+4) goto Fail; + err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], NULL, argv[optind+3], argc-(optind+4), argv+(optind+4)); + break; + + case 'P': if (argc < optind+6) goto Fail; + err = RegisterProxyAddressRecord(&client2, argv[optind+4], argv[optind+5]); + if (err) break; + err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], argv[optind+4], argv[optind+3], argc-(optind+6), argv+(optind+6)); + break; + + case 'Q': + case 'C': { + uint16_t rrtype, rrclass; + DNSServiceFlags flags = kDNSServiceFlagsReturnCNAME; + if (argc < optind+1) goto Fail; + rrtype = (argc <= optind+1) ? kDNSServiceType_A : GetRRType(argv[optind+1]); + rrclass = (argc <= optind+2) ? kDNSServiceClass_IN : atoi(argv[optind+2]); + if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery; + err = DNSServiceQueryRecord(&client, flags, opinterface, argv[optind+0], rrtype, rrclass, qr_reply, NULL); + break; + } + + case 'A': + case 'U': + case 'N': { + Opaque16 registerPort = { { 0x12, 0x34 } }; + static const char TXT[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; + printf("Registering Service Test._testupdate._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testupdate._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL); + break; + } + + case 'T': { + Opaque16 registerPort = { { 0x23, 0x45 } }; + char TXT[1024]; + unsigned int i; + for (i=0; i<sizeof(TXT); i++) + if ((i & 0x1F) == 0) TXT[i] = 0x1F; else TXT[i] = 'A' + (i >> 5); + printf("Registering Service Test._testlargetxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testlargetxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT), TXT, reg_reply, NULL); + break; + } + + case 'M': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT1[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; + static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String" "\xC" "Sixth String"; + printf("Registering Service Test._testdualtxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL); + if (!err) err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0); + break; + } + + case 'I': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT[] = "\x09" "Test Data"; + printf("Registering Service Test._testtxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); + if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0); + break; + } + + default: goto Fail; + } + + if (!client || err != kDNSServiceErr_NoError) { fprintf(stderr, "DNSService call failed %ld\n", (long int)err); return (-1); } + HandleEvents(); + + // Be sure to deallocate the DNSServiceRef when you're finished + if (client ) DNSServiceRefDeallocate(client ); + if (client2) DNSServiceRefDeallocate(client2); + return 0; + +Fail: + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", a0); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", a0); + fprintf(stderr, "%s -B <Type> <Domain> (Browse for services instances)\n", a0); + fprintf(stderr, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", a0); + fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", a0); + fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...] (Proxy)\n", a0); + fprintf(stderr, "%s -Q <FQDN> <rrtype> <rrclass> (Generic query for any record type)\n", a0); + fprintf(stderr, "%s -C <FQDN> <rrtype> <rrclass> (Query; reconfirming each result)\n", a0); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", a0); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", a0); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", a0); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", a0); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", a0); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", a0); + return 0; + } diff --git a/usr/src/cmd/cmd-inet/usr.lib/Makefile b/usr/src/cmd/cmd-inet/usr.lib/Makefile index b6fb9957a0..4bc772a574 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile @@ -27,7 +27,8 @@ SUBDIRS= dhcp dsvclockd in.chargend in.daytimed \ in.discardd in.echod in.dhcpd in.mpathd in.ndpd \ - in.ripngd in.timed inetd ncaconfd pppoe slpd wanboot wpad + in.ripngd in.timed inetd mdnsd ncaconfd pppoe \ + slpd wanboot wpad MSGSUBDIRS= dsvclockd in.dhcpd inetd ncaconfd wanboot diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c new file mode 100644 index 0000000000..73801fc781 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c @@ -0,0 +1,2181 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSCommon.c,v $ +Revision 1.100.2.1 2006/08/29 06:24:22 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.100 2006/06/08 22:58:46 cheshire +<rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16 + +Revision 1.99 2006/05/18 01:32:33 cheshire +<rdar://problem/4472706> iChat: Lost connection with Bonjour +(mDNSResponder insufficiently defensive against malformed browsing PTR responses) + +Revision 1.98 2006/03/19 17:00:58 cheshire +Define symbol MaxMsg instead of using hard-coded constant value '80' + +Revision 1.97 2006/03/18 21:47:56 cheshire +<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions + +Revision 1.96 2006/03/10 21:51:42 cheshire +<rdar://problem/4111464> After record update, old record sometimes remains in cache +Split out SameRDataBody() into a separate routine so it can be called from other code + +Revision 1.95 2006/03/08 22:43:11 cheshire +Use "localdomain" symbol instead of literal string + +Revision 1.94 2006/03/02 21:59:55 cheshire +<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use" +Improve sanity checks & debugging support in GetLargeResourceRecord() + +Revision 1.93 2006/03/02 20:30:47 cheshire +Improved GetRRDisplayString to also show priority, weight, and port for SRV records + +Revision 1.92 2005/09/16 21:06:49 cheshire +Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place + +Revision 1.91 2005/07/10 22:10:37 cheshire +The getOptRdata routine implicitly assumes the destination ResourceRecord is large enough to +hold MaximumRDSize bytes, but its parameter was a generic ResourceRecord, which need not be that +large. Changing the parameter to a LargeCacheRecord makes it clearer what the routine requires. + +Revision 1.90 2005/03/21 00:33:51 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.89 2005/03/17 18:59:38 ksekar +<rdar://problem/4012279> Properly parse multiple LLQ Options per packet on Windows + +Revision 1.88 2005/03/16 00:42:32 ksekar +<rdar://problem/4012279> Long-lived queries not working on Windows + +Revision 1.87 2005/02/25 04:21:00 cheshire +<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing + +Revision 1.86 2005/02/18 00:43:12 cheshire +<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long + +Revision 1.85 2005/02/10 22:35:17 cheshire +<rdar://problem/3727944> Update name + +Revision 1.84 2005/02/03 00:44:38 cheshire +<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL + +Revision 1.83 2005/01/27 22:57:55 cheshire +Fix compile errors on gcc4 + +Revision 1.82 2005/01/19 03:27:03 cheshire +<rdar://problem/3961051> CPU Spin in mDNSResponder +GetNextScheduledEvent() needs to check LocalRecordReady() + +Revision 1.81 2004/12/18 03:13:45 cheshire +<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records + +Revision 1.80 2004/12/16 21:46:43 cheshire +Add DNSTypeName case for kDNSType_SOA + +Revision 1.79 2004/12/16 21:38:37 cheshire +Add DNSTypeName case for kDNSType_NS + +Revision 1.78 2004/12/16 21:27:37 ksekar +Fixed build failures when compiled with verbose debugging messages + +Revision 1.77 2004/12/16 20:12:59 cheshire +<rdar://problem/3324626> Cache memory management improvements + +Revision 1.76 2004/12/16 08:05:29 shersche +Remove extranenous semicolons that cause compilation errors on Windows + +Revision 1.75 2004/12/15 02:11:22 ksekar +<rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness + +Revision 1.74 2004/12/09 22:49:15 ksekar +<rdar://problem/3913653> Wide-Area Goodbyes broken + +Revision 1.73 2004/12/07 22:49:06 cheshire +<rdar://problem/3908850> BIND doesn't allow zero-length TXT records + +Revision 1.72 2004/12/06 21:15:20 ksekar +<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.71 2004/12/04 02:12:45 cheshire +<rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.70 2004/12/03 19:52:44 ksekar +Use PutResourceRecordTTLJumbo for putDeletionRecord() + +Revision 1.69 2004/12/03 07:20:50 ksekar +<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails + +Revision 1.68 2004/11/24 00:10:43 cheshire +<rdar://problem/3869241> For unicast operations, verify that service types are legal + +Revision 1.67 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.66 2004/10/23 01:16:00 cheshire +<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts + +Revision 1.65 2004/10/20 02:15:09 cheshire +Add case in GetRRDisplayString() to display NS rdata + +Revision 1.64 2004/10/13 00:24:02 cheshire +Disable "array is too small to include a terminating null character" warning on Windows + +Revision 1.63 2004/10/10 06:57:14 cheshire +Change definition of "localdomain" to make code compile a little smaller + +Revision 1.62 2004/10/06 01:44:19 cheshire +<rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record + +Revision 1.61 2004/09/30 00:24:56 ksekar +<rdar://problem/3695802> Dynamically update default registration domains on config change + +Revision 1.60 2004/09/27 23:25:30 cheshire +Fix compiler warning: soa.serial is signed, not unsigned + +Revision 1.59 2004/09/27 22:53:45 ksekar +Fixed getLargeResourceRecord for SOA rdata. + +Revision 1.58 2004/09/25 02:41:39 cheshire +<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events + +Revision 1.57 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.56 2004/09/24 20:57:39 cheshire +<rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors + +Revision 1.55 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.54 2004/09/17 00:49:51 cheshire +Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use +is GetLargeResourceRecord + +Revision 1.53 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.52 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.51 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.50 2004/09/16 01:58:14 cheshire +Fix compiler warnings + +Revision 1.49 2004/09/14 23:42:35 cheshire +<rdar://problem/3801296> Need to seed random number generator from platform-layer data + +Revision 1.48 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.47 2004/08/25 02:50:04 cheshire +<rdar://problem/3561220> Browses are no longer piggybacking on other browses +Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal + +Revision 1.46 2004/08/18 17:35:40 ksekar +<rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.45 2004/08/15 18:26:00 cheshire +Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + +Revision 1.44 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.43 2004/08/12 02:55:46 ksekar +Fix param order error moving putPrereqNameNotInUse from uDNS.c using +ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy(). + +Revision 1.42 2004/08/10 23:19:14 ksekar +<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.41 2004/08/10 01:10:01 cheshire +<rdar://problem/3588761> Current method of doing subtypes causes name collisions +Minor revision from Roger Pantos + +Revision 1.40 2004/08/04 22:10:46 cheshire +<rdar://problem/3588761> Current method of doing subtypes causes name collisions +Change to use "._sub." instead of ".s." to mark subtypes. + +Revision 1.39 2004/07/13 21:24:24 rpantos +Fix for <rdar://problem/3701120>. + +Revision 1.38 2004/06/18 21:08:58 cheshire +<rdar://problem/3540040> Applications are registering invalid records +Attempts to create domain names like "www..apple.com." now logged to aid debugging + +Revision 1.37 2004/06/18 20:25:42 cheshire +<rdar://problem/3488547> Add a syslog message if someone tries to use "local.arpa". + +Revision 1.36 2004/06/18 19:09:59 cheshire +<rdar://problem/3588761> Current method of doing subtypes causes name collisions + +Revision 1.35 2004/06/05 00:14:44 cheshire +Fix signed/unsigned and other compiler warnings + +Revision 1.34 2004/06/04 00:25:25 cheshire +Fix misaligned write exception that occurs on some platforms + +Revision 1.33 2004/06/04 00:16:18 cheshire +Remove non-portable use of 'inline' + +Revision 1.32 2004/06/03 03:09:58 ksekar +<rdar://problem/3668626>: Garbage Collection for Dynamic Updates + +Revision 1.31 2004/05/28 23:42:36 ksekar +<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.30 2004/05/26 09:08:04 bradley +Added cast to correct structure pointer when allocating domain name list element to fix C++ builds. + +Revision 1.29 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.28 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.27 2004/04/22 20:29:07 cheshire +Log error message if no count field passed to PutResourceRecordTTL() + +Revision 1.26 2004/04/22 04:07:01 cheshire +Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it + +Revision 1.25 2004/04/22 03:05:28 cheshire +kDNSClass_ANY should be kDNSQClass_ANY + +Revision 1.24 2004/04/22 02:51:20 cheshire +Use common code for HINFO/TXT and TSIG cases in putRData + +Revision 1.23 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.22 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.21 2004/04/09 16:47:28 cheshire +<rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND + +Revision 1.20 2004/04/09 16:37:15 cheshire +Suggestion from Bob Bradley: +Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers + +Revision 1.19 2004/04/02 19:34:38 cheshire +Fix broken comment + +Revision 1.18 2004/03/30 06:45:00 cheshire +Compiler warning fixes from Don Woodward at Roku Labs + +Revision 1.17 2004/03/19 22:25:20 cheshire +<rdar://problem/3579561>: Need to limit service types to fourteen characters +Won't actually do this for now, but keep the code around just in case + +Revision 1.16 2004/03/08 02:45:35 cheshire +Minor change to make a couple of the log messages a bit shorter + +Revision 1.15 2004/03/08 02:44:09 cheshire +<rdar://problem/3579561>: Need to limit service types to fourteen characters + +Revision 1.14 2004/02/21 02:06:24 cheshire +Can't use anonymous unions -- they're non-standard and don't work on all compilers + +Revision 1.13 2004/02/06 23:04:18 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.12 2004/02/03 22:37:10 cheshire +Delete unused (commented-out) code + +Revision 1.11 2004/02/03 22:35:34 cheshire +<rdar://problem/3548256>: Should not allow empty string for resolve domain + +Revision 1.10 2004/02/03 19:47:36 ksekar +Added an asynchronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.9 2004/01/27 20:15:22 cheshire +<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 + +Revision 1.8 2004/01/24 23:24:36 cheshire +Expanded out the list of local domains to reduce risk of mistakes in future + +Revision 1.7 2004/01/24 08:32:30 bradley +Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds. +Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting +to signal potentially hidden errors about the subsequent digit not being part of the octal sequence. + +Revision 1.6 2004/01/24 04:59:15 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.5 2004/01/23 23:23:14 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.4 2004/01/22 02:15:33 cheshire +<rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast + +Revision 1.3 2004/01/21 21:16:29 cheshire +Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc. + +Revision 1.2 2003/12/13 05:47:48 bradley +Made local ptr const to fix error when assigning from const structure. Disable benign conditional +expression is constant warning when building with Microsoft compilers. + +Revision 1.1 2003/12/13 03:05:27 ksekar +<rdar://problem/3192548>: DynDNS: Unicast query of service records + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +// Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary +#define mDNS_InstantiateInlines 1 +#include "DNSCommon.h" + +// Disable certain benign warnings with Microsoft compilers +#if (defined(_MSC_VER)) + // Disable "conditional expression is constant" warning for debug macros. + // Otherwise, this generates warnings for the perfectly natural construct "while(1)" + // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know + #pragma warning(disable:4127) + // Disable "array is too small to include a terminating null character" warning + // -- domain labels have an initial length byte, not a terminating null character + #pragma warning(disable:4295) +#endif + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNameList copy/deallocation routines +#endif + +mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig) + { + DNameListElem *copy = mDNSNULL, *newelem; + const DNameListElem *ptr; + + for (ptr = orig; ptr; ptr = ptr->next) + { + newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem)); + if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; } + AssignDomainName(&newelem->name, &ptr->name); + newelem->next = copy; + copy = newelem; + } + return copy; + } + +mDNSexport void mDNS_FreeDNameList(DNameListElem *list) + { + DNameListElem *fptr; + + while (list) + { + fptr = list; + list = list->next; + mDNSPlatformMemFree(fptr); + } + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Utility Functions +#endif + +// return true for RFC1918 private addresses +mDNSexport mDNSBool IsPrivateV4Addr(mDNSAddr *addr) + { + mDNSu8 *b; + + if (addr->type != mDNSAddrType_IPv4) return mDNSfalse; + b = addr->ip.v4.b; + + return ((b[0] == 10) || // 10/8 prefix + (b[0] == 172 && b[1] > 15 && b[1] < 32) || // 172.16/12 + (b[0] == 192 && b[1] == 168)); // 192.168/16 + } + +mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf) + { + while (intf && !intf->InterfaceActive) intf = intf->next; + return(intf); + } + +mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf) + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + if (next) return(next->InterfaceID); else return(mDNSNULL); + } + +mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id) + { + mDNSu32 slot, used = 0; + CacheGroup *cg; + CacheRecord *rr; + FORALL_CACHERECORDS(slot, cg, rr) + if (rr->resrec.InterfaceID == id) used++; + return(used); + } + +mDNSexport char *DNSTypeName(mDNSu16 rrtype) + { + switch (rrtype) + { + case kDNSType_A: return("Addr"); + case kDNSType_NS: return("NS"); + case kDNSType_CNAME:return("CNAME"); + case kDNSType_SOA: return("SOA"); + case kDNSType_NULL: return("NULL"); + case kDNSType_PTR: return("PTR"); + case kDNSType_HINFO:return("HINFO"); + case kDNSType_TXT: return("TXT"); + case kDNSType_AAAA: return("AAAA"); + case kDNSType_SRV: return("SRV"); + case kDNSQType_ANY: return("ANY"); + default: { + static char buffer[16]; + mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype); + return(buffer); + } + } + } + +// Note slight bug: this code uses the rdlength from the ResourceRecord object, to display +// the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as +// long as this routine is only used for debugging messages, it probably isn't a big problem. +mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer) + { + #define Max (MaxMsg-1) + char *ptr = buffer; + mDNSu32 length = mDNS_snprintf(buffer, Max, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype)); + switch (rr->rrtype) + { + case kDNSType_A: mDNS_snprintf(buffer+length, Max-length, "%.4a", &rd->ipv4); break; + + case kDNSType_NS: // Same as PTR + case kDNSType_CNAME:// Same as PTR + case kDNSType_PTR: mDNS_snprintf(buffer+length, Max-length, "%##s", rd->name.c); break; + + case kDNSType_HINFO:// Display this the same as TXT (just show first string) + case kDNSType_TXT: mDNS_snprintf(buffer+length, Max-length, "%#s", rd->txt.c); break; + + case kDNSType_AAAA: mDNS_snprintf(buffer+length, Max-length, "%.16a", &rd->ipv6); break; + case kDNSType_SRV: mDNS_snprintf(buffer+length, Max-length, "%u %u %u %##s", + rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break; + default: mDNS_snprintf(buffer+length, Max-length, "RDLen %d: %s", rr->rdlength, rd->data); break; + } + for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.'; + return(buffer); + } + +mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) + { + static mDNSu32 seed = 0; + mDNSu32 mask = 1; + + if (!seed) + { + int i; + seed = mDNSPlatformRandomSeed(); // Pick an initial seed + for (i=0; i<100; i++) seed = seed * 21 + 1; // And mix it up a bit + } + while (mask < max) mask = (mask << 1) | 1; + do seed = seed * 21 + 1; while ((seed & mask) > max); + return (seed & mask); + } + +mDNSexport mDNSu32 mDNSRandomFromFixedSeed(mDNSu32 seed, mDNSu32 max) + { + mDNSu32 mask = 1; + while (mask < max) mask = (mask << 1) | 1; + do seed = seed * 21 + 1; while ((seed & mask) > max); + return (seed & mask); + } + +mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2) + { + if (ip1->type == ip2->type) + { + switch (ip1->type) + { + case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal + case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4)); + case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6)); + } + } + return(mDNSfalse); + } + +mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) + { + switch(ip->type) + { + case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroupv4.NotAnInteger); + case mDNSAddrType_IPv6: return(mDNSBool)(ip->ip.v6.l[0] == AllDNSLinkGroupv6.l[0] && + ip->ip.v6.l[1] == AllDNSLinkGroupv6.l[1] && + ip->ip.v6.l[2] == AllDNSLinkGroupv6.l[2] && + ip->ip.v6.l[3] == AllDNSLinkGroupv6.l[3] ); + default: return(mDNSfalse); + } + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Domain Name Utility Functions +#endif + +mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b) + { + int i; + const int len = *a++; + + if (len > MAX_DOMAIN_LABEL) + { debugf("Malformed label (too long)"); return(mDNSfalse); } + + if (len != *b++) return(mDNSfalse); + for (i=0; i<len; i++) + { + mDNSu8 ac = *a++; + mDNSu8 bc = *b++; + if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; + if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; + if (ac != bc) return(mDNSfalse); + } + return(mDNStrue); + } + +mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2) + { + const mDNSu8 * a = d1->c; + const mDNSu8 * b = d2->c; + const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid + + while (*a || *b) + { + if (a + 1 + *a >= max) + { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); } + if (!SameDomainLabel(a, b)) return(mDNSfalse); + a += 1 + *a; + b += 1 + *b; + } + + return(mDNStrue); + } + +mDNSexport mDNSBool IsLocalDomain(const domainname *d) + { + // Domains that are defined to be resolved via link-local multicast are: + // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa. + static const domainname *nL = (domainname*)"\x5" "local"; + static const domainname *nR = (domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa"; + static const domainname *n8 = (domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *n9 = (domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *nA = (domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + static const domainname *nB = (domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + + const domainname *d1, *d2, *d3, *d4, *d5, *d6; // Top-level domain, second-level domain, etc. + d1 = d2 = d3 = d4 = d5 = d6 = mDNSNULL; + while (d->c[0]) + { + d6 = d5; d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d; + d = (domainname*)(d->c + 1 + d->c[0]); + } + + if (d1 && SameDomainName(d1, nL)) return(mDNStrue); + if (d4 && SameDomainName(d4, nR)) return(mDNStrue); + if (d6 && SameDomainName(d6, n8)) return(mDNStrue); + if (d6 && SameDomainName(d6, n9)) return(mDNStrue); + if (d6 && SameDomainName(d6, nA)) return(mDNStrue); + if (d6 && SameDomainName(d6, nB)) return(mDNStrue); + return(mDNSfalse); + } + +// Returns length of a domain name INCLUDING the byte for the final null label +// i.e. for the root label "." it returns one +// For the FQDN "com." it returns 5 (length byte, three data bytes, final zero) +// Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME) +// If the given domainname is invalid, result is 256 +mDNSexport mDNSu16 DomainNameLength(const domainname *const name) + { + const mDNSu8 *src = name->c; + while (*src) + { + if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); + src += 1 + *src; + if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); + } + return((mDNSu16)(src - name->c + 1)); + } + +// CompressedDomainNameLength returns the length of a domain name INCLUDING the byte +// for the final null label i.e. for the root label "." it returns one. +// E.g. for the FQDN "foo.com." it returns 9 +// (length, three data bytes, length, three more data bytes, final zero). +// In the case where a parent domain name is provided, and the given name is a child +// of that parent, CompressedDomainNameLength returns the length of the prefix portion +// of the child name, plus TWO bytes for the compression pointer. +// E.g. for the name "foo.com." with parent "com.", it returns 6 +// (length, three data bytes, two-byte compression pointer). +mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent) + { + const mDNSu8 *src = name->c; + if (parent && parent->c[0] == 0) parent = mDNSNULL; + while (*src) + { + if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); + if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu16)(src - name->c + 2)); + src += 1 + *src; + if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); + } + return((mDNSu16)(src - name->c + 1)); + } + +// AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname. +// The C string contains the label as-is, with no escaping, etc. +// Any dots in the name are literal dots, not label separators +// If successful, AppendLiteralLabelString returns a pointer to the next unused byte +// in the domainname bufer (i.e., the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// AppendLiteralLabelString returns mDNSNULL. +mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr) + { + mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name + const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) + const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL; + const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2; + mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go + + while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data + *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte + *ptr++ = 0; // Put the null root label on the end + if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input + else return(ptr); // Success: return new value of ptr + } + +// AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname. +// The C string is in conventional DNS syntax: +// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots. +// If successful, AppendDNSNameString returns a pointer to the next unused byte +// in the domainname bufer (i.e., the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// AppendDNSNameString returns mDNSNULL. +mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring) + { + const char *cstr = cstring; + mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name + const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) + while (*cstr && ptr < lim) // While more characters, and space to put them... + { + mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go + if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); } + while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... + { + mDNSu8 c = (mDNSu8)*cstr++; // Read the character + if (c == '\\') // If escape character, check next character + { + c = (mDNSu8)*cstr++; // Assume we'll just take the next character + if (mdnsIsDigit(cstr[-1]) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1])) + { // If three decimal digits, + int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal + int v1 = cstr[ 0] - '0'; + int v2 = cstr[ 1] - '0'; + int val = v0 * 100 + v1 * 10 + v2; + if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it + } + } + *ptr++ = c; // Write the character + } + if (*cstr) cstr++; // Skip over the trailing dot (if present) + if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort + return(mDNSNULL); + *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte + } + + *ptr++ = 0; // Put the null root label on the end + if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input + else return(ptr); // Success: return new value of ptr + } + +// AppendDomainLabel appends a single label to a name. +// If successful, AppendDomainLabel returns a pointer to the next unused byte +// in the domainname bufer (i.e., the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// AppendDomainLabel returns mDNSNULL. +mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label) + { + int i; + mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; + + // Check label is legal + if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL); + + // Check that ptr + length byte + data bytes + final zero does not exceed our limit + if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL); + + for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data + *ptr++ = 0; // Put the null root label on the end + return(ptr); + } + +mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append) + { + mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name + const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) + const mDNSu8 * src = append->c; + while(src[0]) + { + int i; + if (ptr + 1 + src[0] > lim) return(mDNSNULL); + for (i=0; i<=src[0]; i++) *ptr++ = src[i]; + *ptr = 0; // Put the null root label on the end + src += i; + } + return(ptr); + } + +// MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping). +// If successful, MakeDomainLabelFromLiteralString returns mDNStrue. +// If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then +// MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse. +// In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored. +// In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result. +mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr) + { + mDNSu8 * ptr = label->c + 1; // Where we're putting it + const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put + while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label + label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte + return(*cstr == 0); // Return mDNStrue if we successfully consumed all input + } + +// MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string. +// The C string is in conventional DNS syntax: +// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots. +// If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte +// in the domainname bufer (i.e., the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// MakeDomainNameFromDNSNameString returns mDNSNULL. +mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr) + { + name->c[0] = 0; // Make an empty domain name + return(AppendDNSNameString(name, cstr)); // And then add this string to it + } + +mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) + { + const mDNSu8 * src = label->c; // Domain label we're reading + const mDNSu8 len = *src++; // Read length of this (non-null) label + const mDNSu8 *const end = src + len; // Work out where the label ends + if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort + while (src < end) // While we have characters in the label + { + mDNSu8 c = *src++; + if (esc) + { + if (c == '.' || c == esc) // If character is a dot or the escape character + *ptr++ = esc; // Output escape character + else if (c <= ' ') // If non-printing ascii, + { // Output decimal escape sequence + *ptr++ = esc; + *ptr++ = (char) ('0' + (c / 100) ); + *ptr++ = (char) ('0' + (c / 10) % 10); + c = (mDNSu8)('0' + (c ) % 10); + } + } + *ptr++ = (char)c; // Copy the character + } + *ptr = 0; // Null-terminate the string + return(ptr); // and return + } + +// Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes) +mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) + { + const mDNSu8 *src = name->c; // Domain name we're reading + const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid + + if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot + + while (*src) // While more characters in the domain name + { + if (src + 1 + *src >= max) return(mDNSNULL); + ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); + if (!ptr) return(mDNSNULL); + src += 1 + *src; + *ptr++ = '.'; // Write the dot after the label + } + + *ptr++ = 0; // Null-terminate the string + return(ptr); // and return + } + +// RFC 1034 rules: +// Host names must start with a letter, end with a letter or digit, +// and have as interior characters only letters, digits, and hyphen. +// This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit + +mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel) + { + const mDNSu8 * src = &UTF8Name[1]; + const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0]; + mDNSu8 * ptr = &hostlabel->c[1]; + const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL; + while (src < end) + { + // Delete apostrophes from source name + if (src[0] == '\'') { src++; continue; } // Standard straight single quote + if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99) + { src += 3; continue; } // Unicode curly apostrophe + if (ptr < lim) + { + if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src; + else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-'; + } + src++; + } + while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks + hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]); + } + +mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, + const domainlabel *name, const domainname *type, const domainname *const domain) + { + int i, len; + mDNSu8 *dst = fqdn->c; + const mDNSu8 *src; + const char *errormsg; + + // In the case where there is no name (and ONLY in that case), + // a single-label subtype is allowed as the first label of a three-part "type" + if (!name && type) + { + const mDNSu8 *s0 = type->c; + if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) + { + const mDNSu8 * s1 = s0 + 1 + s0[0]; + if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63) + { + const mDNSu8 *s2 = s1 + 1 + s1[0]; + if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels + { + static const mDNSu8 SubTypeLabel[5] = "\x04_sub"; + src = s0; // Copy the first label + len = *src; + for (i=0; i <= len; i++) *dst++ = *src++; + for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i]; + type = (domainname *)s1; + + // Special support for queries done by some third-party network monitoring software + // For these queries, we retract the "._sub" we just added between the subtype and the main type + if (SameDomainName((domainname*)s0, (domainname*)"\x09_services\x07_dns-sd\x04_udp") || + SameDomainName((domainname*)s0, (domainname*)"\x09_services\x05_mdns\x04_udp")) + dst -= sizeof(SubTypeLabel); + } + } + } + } + + if (name && name->c[0]) + { + src = name->c; // Put the service name into the domain name + len = *src; + if (len >= 0x40) { errormsg="Service instance name too long"; goto fail; } + for (i=0; i<=len; i++) *dst++ = *src++; + } + else + name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below + + src = type->c; // Put the service type into the domain name + len = *src; + if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, &localdomain))) + { + errormsg="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>"; + goto fail; + } + if (src[1] != '_') { errormsg="Application protocol name must begin with underscore"; goto fail; } + for (i=2; i<=len; i++) + if (!mdnsIsLetter(src[i]) && !mdnsIsDigit(src[i]) && src[i] != '-' && src[i] != '_') + { errormsg="Application protocol name must contain only letters, digits, and hyphens"; goto fail; } + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + if (!(len == 4 && src[1] == '_' && + (((src[2] | 0x20) == 'u' && (src[3] | 0x20) == 'd') || ((src[2] | 0x20) == 't' && (src[3] | 0x20) == 'c')) && + (src[4] | 0x20) == 'p')) + { errormsg="Transport protocol name must be _udp or _tcp"; goto fail; } + for (i=0; i<=len; i++) *dst++ = *src++; + + if (*src) { errormsg="Service type must have only two labels"; goto fail; } + + *dst = 0; + if (!domain->c[0]) { errormsg="Service domain must be non-empty"; goto fail; } + if (SameDomainName(domain, (domainname*)"\x05" "local" "\x04" "arpa")) + { errormsg="Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; } + dst = AppendDomainName(fqdn, domain); + if (!dst) { errormsg="Service domain too long"; goto fail; } + return(dst); + +fail: + LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c); + return(mDNSNULL); + } + +// A service name has the form: instance.application-protocol.transport-protocol.domain +// DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character +// set or length limits for the protocol names, and the final domain is allowed to be empty. +// However, if the given FQDN doesn't contain at least three labels, +// DeconstructServiceName will reject it and return mDNSfalse. +mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn, + domainlabel *const name, domainname *const type, domainname *const domain) + { + int i, len; + const mDNSu8 *src = fqdn->c; + const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME; + mDNSu8 *dst; + + dst = name->c; // Extract the service name + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); } + if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + + dst = type->c; // Extract the service type + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); } + if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); } + if (len >= 0x40) { debugf("DeconstructServiceName: Transport protocol name too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + *dst++ = 0; // Put terminator on the end of service type + + dst = domain->c; // Extract the service domain + while (*src) + { + len = *src; + if (len >= 0x40) + { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); } + if (src + 1 + len + 1 >= max) + { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + } + *dst++ = 0; // Put the null root label on the end + + return(mDNStrue); + } + +// Notes on UTF-8: +// 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F +// 10xxxxxx is a continuation byte of a multi-byte character +// 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1) +// 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1) +// 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1) +// 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1) +// 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1) +// +// UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF. +// Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive +// about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?") +// The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8), +// and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8). + +mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max) + { + if (length > max) + { + mDNSu8 c1 = string[max]; // First byte after cut point + mDNSu8 c2 = (max+1 < length) ? string[max+1] : 0xB0; // Second byte after cut point + length = max; // Trim length down + while (length > 0) + { + // Check if the byte right after the chop point is a UTF-8 continuation byte, + // or if the character right after the chop point is the second of a UTF-16 surrogate pair. + // If so, then we continue to chop more bytes until we get to a legal chop point. + mDNSBool continuation = ((c1 & 0xC0) == 0x80); + mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0); + if (!continuation && !secondsurrogate) break; + c2 = c1; + c1 = string[--length]; + } + // Having truncated characters off the end of our string, also cut off any residual white space + while (length > 0 && string[length-1] <= ' ') length--; + } + return(length); + } + +// Returns true if a rich text label ends in " (nnn)", or if an RFC 1034 +// name ends in "-nnn", where n is some decimal number. +mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText) + { + mDNSu16 l = name->c[0]; + + if (RichText) + { + if (l < 4) return mDNSfalse; // Need at least " (2)" + if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')' + if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit + l--; + while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits + return (name->c[l] == '(' && name->c[l - 1] == ' '); + } + else + { + if (l < 2) return mDNSfalse; // Need at least "-2" + if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit + l--; + while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits + return (name->c[l] == '-'); + } + } + +// removes an auto-generated suffix (appended on a name collision) from a label. caller is +// responsible for ensuring that the label does indeed contain a suffix. returns the number +// from the suffix that was removed. +mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText) + { + mDNSu32 val = 0, multiplier = 1; + + // Chop closing parentheses from RichText suffix + if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--; + + // Get any existing numerical suffix off the name + while (mdnsIsDigit(name->c[name->c[0]])) + { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; } + + // Chop opening parentheses or dash from suffix + if (RichText) + { + if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2; + } + else + { + if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1; + } + + return(val); + } + +// appends a numerical suffix to a label, with the number following a whitespace and enclosed +// in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label). +mDNSexport void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText) + { + mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2") + if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)") + + // Truncate trailing spaces from RichText names + if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--; + + while (val >= divisor * 10) { divisor *= 10; chars++; } + + name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars); + + if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; } + else { name->c[++name->c[0]] = '-'; } + + while (divisor) + { + name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor); + val %= divisor; + divisor /= 10; + } + + if (RichText) name->c[++name->c[0]] = ')'; + } + +mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) + { + mDNSu32 val = 0; + + if (LabelContainsSuffix(name, RichText)) + val = RemoveLabelSuffix(name, RichText); + + // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate. + // If existing suffix in the range 2-9, increment it. + // If we've had ten conflicts already, there are probably too many hosts trying to use the same name, + // so add a random increment to improve the chances of finding an available name next time. + if (val == 0) val = 2; + else if (val < 10) val++; + else val += 1 + mDNSRandom(99); + + AppendLabelSuffix(name, val, RichText); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Resource Record Utility Functions +#endif + +mDNSexport mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb) + { + mDNSu32 sum = 0; + int i; + for (i=0; i+1 < rdlength; i+=2) + { + sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1]; + sum = (sum<<3) | (sum>>29); + } + if (i < rdlength) + { + sum += ((mDNSu32)(rdb->data[i])) << 8; + } + return(sum); + } + +// r1 has to be a full ResourceRecord including rrtype and rdlength +// r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1 +mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2) + { + switch(r1->rrtype) + { + case kDNSType_CNAME:// Same as PTR + case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->name)); + + case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->srv.priority && + r1->rdata->u.srv.weight == r2->srv.weight && + r1->rdata->u.srv.port.NotAnInteger == r2->srv.port.NotAnInteger && + SameDomainName(&r1->rdata->u.srv.target, &r2->srv.target) ); + + default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->data, r1->rdlength)); + } + } + +mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2) + { + if (r1->rrtype != r2->rrtype) return(mDNSfalse); + if (r1->rdlength != r2->rdlength) return(mDNSfalse); + if (r1->rdatahash != r2->rdatahash) return(mDNSfalse); + return(SameRDataBody(r1, &r2->rdata->u)); + } + +mDNSexport mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2) + { + return (r1->namehash == r2->namehash && + r1->rrtype == r2->rrtype && + SameDomainName(r1->name, r2->name) && + SameRData(r1, r2)); + } + +mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) + { + if (rr->InterfaceID && + q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && + rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. + if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) return(mDNSfalse); + if ( rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); + } + +mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) + { + const RDataBody *rd = &rr->rdata->u; + const domainname *const name = estimate ? rr->name : mDNSNULL; + switch (rr->rrtype) + { + case kDNSType_A: return(sizeof(rd->ipv4)); + case kDNSType_CNAME:// Same as PTR + case kDNSType_NS: // Same as PTR + case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name)); + case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]); + case kDNSType_NULL: // Same as TXT -- not self-describing, so have to just trust rdlength + case kDNSType_TXT: return(rr->rdlength); // TXT is not self-describing, so have to just trust rdlength + case kDNSType_AAAA: return(sizeof(rd->ipv6)); + case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name)); + case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) + + CompressedDomainNameLength(&rd->soa.rname, name) + + 5 * sizeof(mDNSOpaque32)); + case kDNSType_OPT: return(rr->rdlength); + default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); + return(rr->rdlength); + } + } + +mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd) + { + mDNSu16 len; + + switch(rrtype) + { + case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); + + case kDNSType_NS: // Same as PTR + case kDNSType_MD: // Same as PTR + case kDNSType_MF: // Same as PTR + case kDNSType_CNAME:// Same as PTR + //case kDNSType_SOA not checked + case kDNSType_MB: // Same as PTR + case kDNSType_MG: // Same as PTR + case kDNSType_MR: // Same as PTR + //case kDNSType_NULL not checked (no specified format, so always valid) + //case kDNSType_WKS not checked + case kDNSType_PTR: if (!rdlength) return(mDNSfalse); + len = DomainNameLength(&rd->u.name); + return(len <= MAX_DOMAIN_NAME && rdlength == len); + + case kDNSType_HINFO:// Same as TXT (roughly) + case kDNSType_MINFO:// Same as TXT (roughly) + case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035) + { + const mDNSu8 *ptr = rd->u.txt.c; + const mDNSu8 *end = rd->u.txt.c + rdlength; + while (ptr < end) ptr += 1 + ptr[0]; + return (ptr == end); + } + + case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr)); + + case kDNSType_MX: if (!rdlength) return(mDNSfalse); + len = DomainNameLength(&rd->u.mx.exchange); + return(len <= MAX_DOMAIN_NAME && rdlength == 2+len); + + case kDNSType_SRV: if (!rdlength) return(mDNSfalse); + len = DomainNameLength(&rd->u.srv.target); + return(len <= MAX_DOMAIN_NAME && rdlength == 6+len); + + default: return(mDNStrue); // Allow all other types without checking + } + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - DNS Message Creation Functions +#endif + +mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags) + { + h->id = id; + h->flags = flags; + h->numQuestions = 0; + h->numAnswers = 0; + h->numAuthorities = 0; + h->numAdditionals = 0; + } + +mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname) + { + const mDNSu8 *result = end - *domname - 1; + + if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label + + // This loop examines each possible starting position in packet, starting end of the packet and working backwards + while (result >= base) + { + // If the length byte and first character of the label match, then check further to see + // if this location in the packet will yield a useful name compression pointer. + if (result[0] == domname[0] && result[1] == domname[1]) + { + const mDNSu8 *name = domname; + const mDNSu8 *targ = result; + while (targ + *name < end) + { + // First see if this label matches + int i; + const mDNSu8 *pointertarget; + for (i=0; i <= *name; i++) if (targ[i] != name[i]) break; + if (i <= *name) break; // If label did not match, bail out + targ += 1 + *name; // Else, did match, so advance target pointer + name += 1 + *name; // and proceed to check next label + if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match! + if (*name == 0) break; // If no more labels to match, we failed, so bail out + + // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches + if (targ[0] < 0x40) continue; // If length value, continue to check next label + if (targ[0] < 0xC0) break; // If 40-BF, not valid + if (targ+1 >= end) break; // Second byte not present! + pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1]; + if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet + if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte + targ = pointertarget; + } + } + result--; // We failed to match at this search position, so back up the tentative result pointer and try again + } + return(mDNSNULL); + } + +// Put a string of dot-separated labels as length-prefixed labels +// domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't) +// msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers) +// end points to the end of the message so far +// ptr points to where we want to put the name +// limit points to one byte past the end of the buffer that we must not overrun +// domainname is the name to put +mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, + mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name) + { + const mDNSu8 *const base = (const mDNSu8 *)msg; + const mDNSu8 * np = name->c; + const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid + const mDNSu8 * pointer = mDNSNULL; + const mDNSu8 *const searchlimit = ptr; + + while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message... + { + if (*np > MAX_DOMAIN_LABEL) + { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); } + + // This check correctly allows for the final trailing root label: + // e.g. + // Suppose our domain name is exactly 255 bytes long, including the final trailing root label. + // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local"). + // We know that max will be at name->c[255] + // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our + // six bytes, then exit the loop, write the final terminating root label, and the domain + // name we've written is exactly 255 bytes long, exactly at the correct legal limit. + // If the name is one byte longer, then we fail the "if" test below, and correctly bail out. + if (np + 1 + *np >= max) + { LogMsg("Malformed domain name %##s (more than 255 bytes)", name->c); return(mDNSNULL); } + + if (base) pointer = FindCompressionPointer(base, searchlimit, np); + if (pointer) // Use a compression pointer if we can + { + mDNSu16 offset = (mDNSu16)(pointer - base); + *ptr++ = (mDNSu8)(0xC0 | (offset >> 8)); + *ptr++ = (mDNSu8)( offset & 0xFF); + return(ptr); + } + else // Else copy one label and try again + { + int i; + mDNSu8 len = *np++; + if (ptr + 1 + len >= limit) return(mDNSNULL); + *ptr++ = len; + for (i=0; i<len; i++) *ptr++ = *np++; + } + } + + if (ptr < limit) // If we didn't run out of space + { + *ptr++ = 0; // Put the final root label + return(ptr); // and return + } + + return(mDNSNULL); + } + +mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) + { + ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); + ptr[1] = (mDNSu8)((val ) & 0xFF); + return ptr + sizeof(mDNSOpaque16); + } + +mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val) + { + ptr[0] = (mDNSu8)((val >> 24) & 0xFF); + ptr[1] = (mDNSu8)((val >> 16) & 0xFF); + ptr[2] = (mDNSu8)((val >> 8) & 0xFF); + ptr[3] = (mDNSu8)((val ) & 0xFF); + return ptr + sizeof(mDNSu32); + } + +mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr) + { + int nput = 0; + rdataOpt *opt; + + while (nput < rr->rdlength) + { + // check if space for opt/optlen + if (ptr + (2 * sizeof(mDNSu16)) > limit) goto space_err; + opt = (rdataOpt *)(rr->rdata->u.data + nput); + ptr = putVal16(ptr, opt->opt); + ptr = putVal16(ptr, opt->optlen); + nput += 2 * sizeof(mDNSu16); + if (opt->opt == kDNSOpt_LLQ) + { + if (ptr + LLQ_OPTLEN > limit) goto space_err; + ptr = putVal16(ptr, opt->OptData.llq.vers); + ptr = putVal16(ptr, opt->OptData.llq.llqOp); + ptr = putVal16(ptr, opt->OptData.llq.err); + mDNSPlatformMemCopy(opt->OptData.llq.id, ptr, 8); // 8-byte id + ptr += 8; + ptr = putVal32(ptr, opt->OptData.llq.lease); + nput += LLQ_OPTLEN; + } + else if (opt->opt == kDNSOpt_Lease) + { + if (ptr + sizeof(mDNSs32) > limit) goto space_err; + ptr = putVal32(ptr, opt->OptData.lease); + nput += sizeof(mDNSs32); + } + else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; } + } + + return ptr; + + space_err: + LogMsg("ERROR: putOptRData - out of space"); + return mDNSNULL; + } + +mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr) + { + mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]); + *ptr += sizeof(mDNSOpaque16); + return val; + } + +mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *const limit, LargeCacheRecord *const cr, mDNSu16 pktRDLen) + { + int nread = 0; + ResourceRecord *const rr = &cr->r.resrec; + rdataOpt *opt = (rdataOpt *)rr->rdata->u.data; + + while (nread < pktRDLen && (mDNSu8 *)opt < rr->rdata->u.data + MaximumRDSize - sizeof(rdataOpt)) + { + // space for opt + optlen + if (nread + (2 * sizeof(mDNSu16)) > rr->rdata->MaxRDLength) goto space_err; + opt->opt = getVal16(&ptr); + opt->optlen = getVal16(&ptr); + nread += 2 * sizeof(mDNSu16); + if (opt->opt == kDNSOpt_LLQ) + { + if ((unsigned)(limit - ptr) < LLQ_OPTLEN) goto space_err; + opt->OptData.llq.vers = getVal16(&ptr); + opt->OptData.llq.llqOp = getVal16(&ptr); + opt->OptData.llq.err = getVal16(&ptr); + mDNSPlatformMemCopy(ptr, opt->OptData.llq.id, 8); + ptr += 8; + opt->OptData.llq.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); + if (opt->OptData.llq.lease > 0x70000000UL / mDNSPlatformOneSecond) + opt->OptData.llq.lease = 0x70000000UL / mDNSPlatformOneSecond; + ptr += sizeof(mDNSOpaque32); + nread += LLQ_OPTLEN; + } + else if (opt->opt == kDNSOpt_Lease) + { + if ((unsigned)(limit - ptr) < sizeof(mDNSs32)) goto space_err; + + opt->OptData.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); + if (opt->OptData.lease > 0x70000000UL / mDNSPlatformOneSecond) + opt->OptData.lease = 0x70000000UL / mDNSPlatformOneSecond; + ptr += sizeof(mDNSs32); + nread += sizeof(mDNSs32); + } + else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; } + opt++; // increment pointer into rdatabody + } + + rr->rdlength = pktRDLen; + return ptr; + + space_err: + LogMsg("ERROR: getLLQRdata - out of space"); + return mDNSNULL; + } + +mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr) + { + switch (rr->rrtype) + { + case kDNSType_A: if (rr->rdlength != 4) + { + debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); + return(mDNSNULL); + } + if (ptr + 4 > limit) return(mDNSNULL); + *ptr++ = rr->rdata->u.ipv4.b[0]; + *ptr++ = rr->rdata->u.ipv4.b[1]; + *ptr++ = rr->rdata->u.ipv4.b[2]; + *ptr++ = rr->rdata->u.ipv4.b[3]; + return(ptr); + + case kDNSType_CNAME:// Same as PTR + case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name)); + + case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6)) + { + debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); + return(mDNSNULL); + } + if (ptr + sizeof(rr->rdata->u.ipv6) > limit) return(mDNSNULL); + mDNSPlatformMemCopy(&rr->rdata->u.ipv6, ptr, sizeof(rr->rdata->u.ipv6)); + return(ptr + sizeof(rr->rdata->u.ipv6)); + + case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority & 0xFF); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight & 0xFF); + *ptr++ = rr->rdata->u.srv.port.b[0]; + *ptr++ = rr->rdata->u.srv.port.b[1]; + return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.srv.target)); + case kDNSType_OPT: return putOptRData(ptr, limit, rr); + + default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype); + // Fall through to common code below + case kDNSType_HINFO: + case kDNSType_TXT: + case kDNSType_TSIG: if (ptr + rr->rdlength > limit) return(mDNSNULL); + mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength); + return(ptr + rr->rdlength); + } + } + +mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit) + { + mDNSu8 *endofrdata; + mDNSu16 actualLength; + + if (rr->RecordType == kDNSRecordTypeUnregistered) + { + LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + return(ptr); + } + + ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); + if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rr->rrtype >> 8); + ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); + ptr[2] = (mDNSu8)(rr->rrclass >> 8); + ptr[3] = (mDNSu8)(rr->rrclass & 0xFF); + ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF); + ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF); + ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF); + ptr[7] = (mDNSu8)( ttl & 0xFF); + endofrdata = putRData(msg, ptr+10, limit, rr); + if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); } + + // Go back and fill in the actual number of data bytes we wrote + // (actualLength can be less than rdlength when domain name compression is used) + actualLength = (mDNSu16)(endofrdata - ptr - 10); + ptr[8] = (mDNSu8)(actualLength >> 8); + ptr[9] = (mDNSu8)(actualLength & 0xFF); + + if (count) (*count)++; + else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + return(endofrdata); + } + +mDNSexport mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 + maxttl) + { + if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl; + return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl)); + } + +mDNSexport mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, + mDNSu16 *count, const AuthRecord *rr) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name); + if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type + ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF); + ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class + ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero + ptr[8] = ptr[9] = 0; // RDATA length is zero + (*count)++; + return(ptr + 10); + } + +mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, name); + if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rrtype >> 8); + ptr[1] = (mDNSu8)(rrtype & 0xFF); + ptr[2] = (mDNSu8)(rrclass >> 8); + ptr[3] = (mDNSu8)(rrclass & 0xFF); + msg->h.numQuestions++; + return(ptr+4); + } + +// for dynamic updates +mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, zone); + if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL + *ptr++ = (mDNSu8)(kDNSType_SOA >> 8); + *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF); + *ptr++ = zoneClass.b[0]; + *ptr++ = zoneClass.b[1]; + msg->h.mDNS_numZones++; + return ptr; + } + +// for dynamic updates +mDNSexport mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end) + { + AuthRecord prereq; + + mDNSPlatformMemZero(&prereq, sizeof(AuthRecord)); + mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL); + AssignDomainName(prereq.resrec.name, name); + prereq.resrec.rrtype = kDNSQType_ANY; + prereq.resrec.rrclass = kDNSClass_NONE; + ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); + return ptr; + } + +// for dynamic updates +mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr) + { + mDNSu16 origclass; + // deletion: specify record w/ TTL 0, class NONE + + origclass = rr->rrclass; + rr->rrclass = kDNSClass_NONE; + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0); + rr->rrclass = origclass; + return ptr; + } + +mDNSexport mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype) + { + const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; + mDNSu16 class = kDNSQClass_ANY; + + ptr = putDomainNameAsLabels(msg, ptr, limit, name); + if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rrtype >> 8); + ptr[1] = (mDNSu8)(rrtype & 0xFF); + ptr[2] = (mDNSu8)(class >> 8); + ptr[3] = (mDNSu8)(class & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl + ptr[8] = ptr[9] = 0; // zero rdlength/rdata + + msg->h.mDNS_numUpdates++; + return ptr + 10; + } + +// for dynamic updates +mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name) + { + const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; + mDNSu16 class = kDNSQClass_ANY; + mDNSu16 rrtype = kDNSQType_ANY; + + ptr = putDomainNameAsLabels(msg, ptr, limit, name); + if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rrtype >> 8); + ptr[1] = (mDNSu8)(rrtype & 0xFF); + ptr[2] = (mDNSu8)(class >> 8); + ptr[3] = (mDNSu8)(class & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl + ptr[8] = ptr[9] = 0; // zero rdlength/rdata + + msg->h.mDNS_numUpdates++; + return ptr + 10; + } + +// for dynamic updates +mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) + { + AuthRecord rr; + ResourceRecord *opt = &rr.resrec; + rdataOpt *optRD; + + mDNSPlatformMemZero(&rr, sizeof(AuthRecord)); + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, 0, mDNSNULL, mDNSNULL); + + opt->RecordType = kDNSRecordTypeKnownUnique; // to avoid warnings in other layers + opt->rrtype = kDNSType_OPT; + opt->rdlength = LEASE_OPT_RDLEN; + opt->rdestimate = LEASE_OPT_RDLEN; + + optRD = &rr.resrec.rdata->u.opt; + optRD->opt = kDNSOpt_Lease; + optRD->optlen = sizeof(mDNSs32); + optRD->OptData.lease = lease; + end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, opt, 0); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } + + return end; + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNS Message Parsing Functions +#endif + +mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name) + { + mDNSu32 sum = 0; + const mDNSu8 *c; + + for (c = name->c; c[0] != 0 && c[1] != 0; c += 2) + { + sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) | + (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]); + sum = (sum<<3) | (sum>>29); + } + if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8); + return(sum); + } + +mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength) + { + domainname *target; + if (NewRData) + { + rr->rdata = NewRData; + rr->rdlength = rdlength; + } + // Must not try to get target pointer until after updating rr->rdata + target = GetRRDomainNameTarget(rr); + rr->rdlength = GetRDLength(rr, mDNSfalse); + rr->rdestimate = GetRDLength(rr, mDNStrue); + rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->rdlength, &rr->rdata->u); + } + +mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end) + { + mDNSu16 total = 0; + + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } + + while (1) // Read sequence of labels + { + const mDNSu8 len = *ptr++; // Read length of this label + if (len == 0) return(ptr); // If length is zero, that means this name is complete + switch (len & 0xC0) + { + case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label + { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } + if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label + { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); } + ptr += len; + total += 1 + len; + break; + + case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL); + case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL); + case 0xC0: return(ptr+1); + } + } + } + +// Routine to fetch an FQDN from the DNS message, following compression pointers if necessary. +mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, + domainname *const name) + { + const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers + mDNSu8 *np = name->c; // Name pointer + const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer + + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } + + *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) + + while (1) // Read sequence of labels + { + const mDNSu8 len = *ptr++; // Read length of this label + if (len == 0) break; // If length is zero, that means this name is complete + switch (len & 0xC0) + { + int i; + mDNSu16 offset; + + case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label + { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } + if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label + { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); } + *np++ = len; + for (i=0; i<len; i++) *np++ = *ptr++; + *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) + break; + + case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c); + return(mDNSNULL); + + case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL); + + case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++); + if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers + ptr = (mDNSu8 *)msg + offset; + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); } + if (*ptr & 0xC0) + { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); } + break; + } + } + + if (nextbyte) return(nextbyte); + else return(ptr); + } + +mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) + { + mDNSu16 pktrdlength; + + ptr = skipDomainName(msg, ptr, end); + if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); } + + if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } + pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); + ptr += 10; + if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + + return(ptr + pktrdlength); + } + +mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr) + { + CacheRecord *rr = &largecr->r; + mDNSu16 pktrdlength; + + if (largecr == &m->rec && largecr->r.resrec.RecordType) + LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &largecr->r)); + + rr->next = mDNSNULL; + rr->resrec.name = &largecr->namestorage; + + rr->NextInKAList = mDNSNULL; + rr->TimeRcvd = m ? m->timenow : 0; + rr->DelayDelivery = 0; + rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTime() + rr->LastUsed = m ? m->timenow : 0; + rr->CRActiveQuestion = mDNSNULL; + rr->UnansweredQueries = 0; + rr->LastUnansweredTime= 0; + rr->MPUnansweredQ = 0; + rr->MPLastUnansweredQT= 0; + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; + rr->NextInCFList = mDNSNULL; + + rr->resrec.InterfaceID = InterfaceID; + ptr = getDomainName(msg, ptr, end, rr->resrec.name); + if (!ptr) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL); } + + if (ptr + 10 > end) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } + + rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); + rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask); + rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]); + if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1) + rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond; + // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for + // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly. + pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); + if (ptr[2] & (kDNSClass_UniqueRRSet >> 8)) + RecordType |= kDNSRecordTypePacketUniqueMask; + ptr += 10; + if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record + + rr->resrec.rdata = (RData*)&rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = MaximumRDSize; + + if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c); + + switch (rr->resrec.rrtype) + { + case kDNSType_A: rr->resrec.rdata->u.ipv4.b[0] = ptr[0]; + rr->resrec.rdata->u.ipv4.b[1] = ptr[1]; + rr->resrec.rdata->u.ipv4.b[2] = ptr[2]; + rr->resrec.rdata->u.ipv4.b[3] = ptr[3]; + break; + + case kDNSType_CNAME:// Same as PTR + case kDNSType_NS: + case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.name)) + { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); } + //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength); + break; + + case kDNSType_NULL: //Same as TXT + case kDNSType_HINFO://Same as TXT + case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength) + { + debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)", + DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); + return(mDNSNULL); + } + rr->resrec.rdlength = pktrdlength; + mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength); + break; + + case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6)); + break; + + case kDNSType_SRV: rr->resrec.rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + rr->resrec.rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + rr->resrec.rdata->u.srv.port.b[0] = ptr[4]; + rr->resrec.rdata->u.srv.port.b[1] = ptr[5]; + if (!getDomainName(msg, ptr+6, end, &rr->resrec.rdata->u.srv.target)) + { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); } + //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength); + break; + + case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname); + if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; } + ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname); + if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; } + if (ptr + 0x14 != end) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; } + rr->resrec.rdata->u.soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); + rr->resrec.rdata->u.soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); + rr->resrec.rdata->u.soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); + rr->resrec.rdata->u.soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); + rr->resrec.rdata->u.soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); + break; + + case kDNSType_OPT: getOptRdata(ptr, end, largecr, pktrdlength); break; + + default: if (pktrdlength > rr->resrec.rdata->MaxRDLength) + { + debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)", + rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); + return(mDNSNULL); + } + debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data", + rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype)); + // Note: Just because we don't understand the record type, that doesn't + // mean we fail. The DNS protocol specifies rdlength, so we can + // safely skip over unknown records and ignore them. + // We also grab a binary copy of the rdata anyway, since the caller + // might know how to interpret it even if we don't. + rr->resrec.rdlength = pktrdlength; + mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength); + break; + } + + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); + + // Success! Now fill in RecordType to show this record contains valid data + rr->resrec.RecordType = RecordType; + return(ptr + pktrdlength); + } + +mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) + { + ptr = skipDomainName(msg, ptr, end); + if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); } + if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } + return(ptr+4); + } + +mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, + DNSQuestion *question) + { + question->InterfaceID = InterfaceID; + ptr = getDomainName(msg, ptr, end, &question->qname); + if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); } + if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } + + question->qnamehash = DomainNameHashValue(&question->qname); + question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type + question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class + return(ptr+4); + } + +mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end) + { + int i; + const mDNSu8 *ptr = msg->data; + for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end); + return(ptr); + } + +mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end) + { + int i; + const mDNSu8 *ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end); + return(ptr); + } + +mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end) + { + int i; + const mDNSu8 *ptr = LocateAuthorities(msg, end); + for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end); + return (ptr); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - Packet Sending Functions +#endif + +mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo) + { + mStatus status; + int nsent; + mDNSs32 msglen; + mDNSu8 lenbuf[2]; + mDNSu16 numQuestions = msg->h.numQuestions; + mDNSu16 numAnswers = msg->h.numAnswers; + mDNSu16 numAuthorities = msg->h.numAuthorities; + mDNSu16 numAdditionals = msg->h.numAdditionals; + mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; + + // Put all the integer values in IETF byte-order (MSB first, LSB second) + *ptr++ = (mDNSu8)(numQuestions >> 8); + *ptr++ = (mDNSu8)(numQuestions & 0xFF); + *ptr++ = (mDNSu8)(numAnswers >> 8); + *ptr++ = (mDNSu8)(numAnswers & 0xFF); + *ptr++ = (mDNSu8)(numAuthorities >> 8); + *ptr++ = (mDNSu8)(numAuthorities & 0xFF); + *ptr++ = (mDNSu8)(numAdditionals >> 8); + *ptr++ = (mDNSu8)(numAdditionals & 0xFF); + + if (authInfo) + { + end = DNSDigest_SignMessage(msg, &end, &numAdditionals, authInfo); + if (!end) return mStatus_UnknownErr; + } + + // Send the packet on the wire + + if (sd >= 0) + { + msglen = (mDNSu16)(end - (mDNSu8 *)msg); + lenbuf[0] = (mDNSu8)(msglen >> 8); // host->network byte conversion + lenbuf[1] = (mDNSu8)(msglen & 0xFF); + nsent = mDNSPlatformWriteTCP(sd, (char*)lenbuf, 2); + //!!!KRS make sure kernel is sending these as 1 packet! + if (nsent != 2) goto tcp_error; + nsent = mDNSPlatformWriteTCP(sd, (char *)msg, msglen); + if (nsent != msglen) goto tcp_error; + status = mStatus_NoError; + } + else + { + status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, dst, dstport); + } + + // Put all the integer values back the way they were before we return + msg->h.numQuestions = numQuestions; + msg->h.numAnswers = numAnswers; + msg->h.numAuthorities = numAuthorities; + msg->h.numAdditionals = (mDNSu16)(authInfo ? numAdditionals - 1 : numAdditionals); + + return(status); + + tcp_error: + LogMsg("mDNSSendDNSMessage: error sending message over tcp"); + return mStatus_UnknownErr; + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - RR List Management & Task Management +#endif + +mDNSexport void mDNS_Lock(mDNS *const m) + { + // MUST grab the platform lock FIRST! + mDNSPlatformLock(m); + + // Normally, mDNS_reentrancy is zero and so is mDNS_busy + // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too + // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one + // If mDNS_busy != mDNS_reentrancy that's a bad sign + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // If this is an initial entry into the mDNSCore code, set m->timenow + // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set + if (m->mDNS_busy == 0) + { + if (m->timenow) + LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNS_TimeNow_NoLock(m)); + m->timenow = mDNS_TimeNow_NoLock(m); + if (m->timenow == 0) m->timenow = 1; + } + else if (m->timenow == 0) + { + LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); + m->timenow = mDNS_TimeNow_NoLock(m); + if (m->timenow == 0) m->timenow = 1; + } + + if (m->timenow_last - m->timenow > 0) + { + m->timenow_adjust += m->timenow_last - m->timenow; + LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust); + m->timenow = m->timenow_last; + } + m->timenow_last = m->timenow; + + // Increment mDNS_busy so we'll recognise re-entrant calls + m->mDNS_busy++; + } + +mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) + { + mDNSs32 e = m->timenow + 0x78000000; + if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e); + if (m->NewQuestions) + { + if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering; + else return(m->timenow); + } + if (m->NewLocalOnlyQuestions) return(m->timenow); + if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords)) return(m->timenow); + if (m->SuppressSending) return(m->SuppressSending); +#ifndef UNICAST_DISABLED + if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent; +#endif + if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; + if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; + if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; + if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; + return(e); + } + +mDNSexport void mDNS_Unlock(mDNS *const m) + { + // Decrement mDNS_busy + m->mDNS_busy--; + + // Check for locking failures + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow + if (m->mDNS_busy == 0) + { + m->NextScheduledEvent = GetNextScheduledEvent(m); + if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero"); + m->timenow = 0; + } + + // MUST release the platform lock LAST! + mDNSPlatformUnlock(m); + } diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h new file mode 100644 index 0000000000..717d87702a --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h @@ -0,0 +1,368 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSCommon.h,v $ +Revision 1.34.2.1 2006/08/29 06:24:22 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.34 2006/03/18 21:47:56 cheshire +<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions + +Revision 1.33 2006/03/10 21:51:41 cheshire +<rdar://problem/4111464> After record update, old record sometimes remains in cache +Split out SameRDataBody() into a separate routine so it can be called from other code + +Revision 1.32 2005/03/21 00:33:51 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.31 2005/02/18 00:43:11 cheshire +<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long + +Revision 1.30 2005/01/19 03:12:44 cheshire +Move LocalRecordReady() macro from mDNS.c to DNSCommon.h + +Revision 1.29 2004/12/15 02:11:22 ksekar +<rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness + +Revision 1.28 2004/12/06 21:15:22 ksekar +<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.27 2004/12/03 07:20:50 ksekar +<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails + +Revision 1.26 2004/12/03 05:18:33 ksekar +<rdar://problem/3810596> mDNSResponder needs to return more specific TSIG errors + +Revision 1.25 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.24 2004/10/23 01:16:00 cheshire +<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts + +Revision 1.23 2004/10/03 23:18:58 cheshire +Move address comparison macros from DNSCommon.h to mDNSEmbeddedAPI.h + +Revision 1.22 2004/09/30 00:24:56 ksekar +<rdar://problem/3695802> Dynamically update default registration domains on config change + +Revision 1.21 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.20 2004/09/17 00:49:51 cheshire +Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use +is GetLargeResourceRecord + +Revision 1.19 2004/09/16 21:59:15 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.18 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.17 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.16 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.15 2004/08/10 23:19:14 ksekar +<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.14 2004/05/28 23:42:36 ksekar +<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.13 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.12 2004/04/22 04:03:59 cheshire +Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.11 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.10 2004/03/13 01:57:33 ksekar +<rdar://problem/3192546>: DynDNS: Dynamic update of service records + +Revision 1.9 2004/02/21 08:56:58 bradley +Wrap prototypes with extern "C" for C++ builds. + +Revision 1.8 2004/02/06 23:04:18 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.7 2004/02/03 19:47:36 ksekar +Added an asynchronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.6 2004/01/27 20:15:22 cheshire +<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 + +Revision 1.5 2004/01/24 03:40:56 cheshire +Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSEmbeddedAPI.h so embedded clients can use it + +Revision 1.4 2004/01/24 03:38:27 cheshire +Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.3 2004/01/23 23:23:14 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.2 2004/01/21 21:12:23 cheshire +Add missing newline at end of file to make Unix tools happier + +Revision 1.1 2003/12/13 03:05:27 ksekar +<rdar://problem/3192548>: DynDNS: Unicast query of service records + + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __DNSCOMMON_H_ +#define __DNSCOMMON_H_ + +#include "mDNSEmbeddedAPI.h" + +#ifdef __cplusplus + extern "C" { +#endif + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - DNS Protocol Constants +#endif + +typedef enum + { + kDNSFlag0_QR_Mask = 0x80, // Query or response? + kDNSFlag0_QR_Query = 0x00, + kDNSFlag0_QR_Response = 0x80, + + kDNSFlag0_OP_Mask = 0x78, // Operation type + kDNSFlag0_OP_StdQuery = 0x00, + kDNSFlag0_OP_Iquery = 0x08, + kDNSFlag0_OP_Status = 0x10, + kDNSFlag0_OP_Unused3 = 0x18, + kDNSFlag0_OP_Notify = 0x20, + kDNSFlag0_OP_Update = 0x28, + + kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, + + kDNSFlag0_AA = 0x04, // Authoritative Answer? + kDNSFlag0_TC = 0x02, // Truncated? + kDNSFlag0_RD = 0x01, // Recursion Desired? + kDNSFlag1_RA = 0x80, // Recursion Available? + + kDNSFlag1_Zero = 0x40, // Reserved; must be zero + kDNSFlag1_AD = 0x20, // Authentic Data [RFC 2535] + kDNSFlag1_CD = 0x10, // Checking Disabled [RFC 2535] + + kDNSFlag1_RC = 0x0F, // Response code + kDNSFlag1_RC_NoErr = 0x00, + kDNSFlag1_RC_FmtErr = 0x01, + kDNSFlag1_RC_SrvErr = 0x02, + kDNSFlag1_RC_NXDomain = 0x03, + kDNSFlag1_RC_NotImpl = 0x04, + kDNSFlag1_RC_Refused = 0x05, + kDNSFlag1_RC_YXDomain = 0x06, + kDNSFlag1_RC_YXRRSet = 0x07, + kDNSFlag1_RC_NXRRSet = 0x08, + kDNSFlag1_RC_NotAuth = 0x09, + kDNSFlag1_RC_NotZone = 0x0A + } DNS_Flags; + +typedef enum + { + TSIG_ErrBadSig = 16, + TSIG_ErrBadKey = 17, + TSIG_ErrBadTime = 18 + } TSIG_ErrorCode; + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Utility Functions +#endif + +extern const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf); +extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf); + +extern mDNSu32 mDNSRandom(mDNSu32 max); +extern mDNSu32 mDNSRandomFromFixedSeed(mDNSu32 seed, mDNSu32 max); + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Domain Name Utility Functions +#endif + +#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') +#define mDNSIsUpperCase(X) ((X) >= 'A' && (X) <= 'Z') +#define mDNSIsLowerCase(X) ((X) >= 'a' && (X) <= 'z') +#define mdnsIsLetter(X) (mDNSIsUpperCase(X) || mDNSIsLowerCase(X)) + +#define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || mdnsIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') ) + +extern mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent); + +extern mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max); +extern mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText); +extern mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText); +extern void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText); +extern void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); +#define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME) + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Resource Record Utility Functions +#endif + +extern mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb); + +extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2); +extern mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2); + +extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); + +extern mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2); + +extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate); + +#define GetRRDomainNameTarget(RR) ( \ + ((RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_NS) \ + ? &(RR)->rdata->u.name : \ + ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL ) + +extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd); +#define LocalRecordReady(X) ((X)->resrec.RecordType != kDNSRecordTypeUnique && (X)->resrec.RecordType != kDNSRecordTypeDeregistering) + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - DNS Message Creation Functions +#endif + +extern void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags); +extern const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname); + +extern mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name); + +extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr); + +// If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes, +// but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet + +extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit); + +#define PutResourceRecordTTL(msg, ptr, count, rr, ttl) PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), \ + ((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? (msg)->data + NormalMaxDNSMessageData : (msg)->data + AbsoluteMaxDNSMessageData) + +#define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), \ + (msg)->data + AbsoluteMaxDNSMessageData) + +extern mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 maxttl); + +extern mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr); + +extern mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass); + +extern mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass); + +extern mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end); + +extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr); + +extern mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype); + +extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name); + +extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); + +#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNS Message Parsing Functions +#endif + +extern mDNSu32 DomainNameHashValue(const domainname *const name); + +extern void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength); + + +extern const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end); + +extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, + domainname *const name); + +extern const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); + +extern const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 * end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr); + +extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); + +extern const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, + DNSQuestion *question); + +extern const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end); + +extern const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end); + +extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end); + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - Packet Sending Functions +#endif + +extern mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo); + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - RR List Management & Task Management +#endif + +extern void mDNS_Lock(mDNS *const m); +extern void mDNS_Unlock(mDNS *const m); + +#ifdef __cplusplus + } +#endif + +#endif // __DNSCOMMON_H_ diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c new file mode 100644 index 0000000000..6d0346c4f2 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c @@ -0,0 +1,1465 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSDigest.c,v $ +Revision 1.15.2.1 2006/08/29 06:24:22 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.15 2006/06/20 04:12:30 cheshire +<rdar://problem/4490961> DNS Update broken + +Revision 1.14 2006/02/25 23:12:07 cheshire +<rdar://problem/4427969> Fix to avoid code generation warning/error on FreeBSD 7 + +Revision 1.13 2004/12/16 20:12:59 cheshire +<rdar://problem/3324626> Cache memory management improvements + +Revision 1.12 2004/12/03 07:20:50 ksekar +<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails + +Revision 1.11 2004/12/02 01:10:27 cheshire +Fix to compile cleanly on 64-bit x86 + +Revision 1.10 2004/11/01 20:36:04 ksekar +<rdar://problem/3802395> mDNSResponder should not receive Keychain Notifications + +Revision 1.9 2004/10/26 09:00:12 cheshire +Save a few bytes by creating HMAC_MD5_AlgName as a C string instead of a 256-byte object + +Revision 1.8 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.7 2004/08/15 18:36:38 cheshire +Don't use strcpy() and strlen() on "struct domainname" objects; +use AssignDomainName() and DomainNameLength() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + +Revision 1.6 2004/06/02 00:17:46 ksekar +Referenced original OpenSSL license headers in source file description. + +Revision 1.5 2004/05/20 18:37:37 cheshire +Fix compiler warnings + +Revision 1.4 2004/04/22 20:28:20 cheshire +Use existing facility of PutResourceRecordTTL() to update count field for us + +Revision 1.3 2004/04/22 03:05:28 cheshire +kDNSClass_ANY should be kDNSQClass_ANY + +Revision 1.2 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.1 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + + + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" + +// Disable certain benign warnings with Microsoft compilers +#if(defined(_MSC_VER)) + // Disable "conditional expression is constant" warning for debug macros. + // Otherwise, this generates warnings for the perfectly natural construct "while(1)" + // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know + #pragma warning(disable:4127) +#endif + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - MD5 Hash Functions +#endif + + +/* The source for the has is derived CommonCrypto files CommonDigest.h, md32_common.h, md5_locl.h, md5_locl.h, and openssl/md5.h. + * The following changes have been made to the original sources: + * replaced CC_LONG w/ mDNSu32 + * replaced CC_MD5* with MD5* + * replaced CC_LONG w/ mDNSu32, removed conditional #defines from md5.h + * removed extern decls for MD5_Init/Update/Final from CommonDigest.h + * removed APPLE_COMMON_DIGEST specific #defines from md5_locl.h + * + * Note: machine archetecure specific conditionals from the original sources are turned off, but are left in the code + * to aid in platform-specific optimizations and debugging. + * Sources originally distributed under the following license headers: + * CommonDigest.c - APSL + * + * md32_Common.h + * ==================================================================== + * Copyright (c) 1999-2002 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * md5_dgst.c, md5_locl.h + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + * Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + * + */ + +//from CommonDigest.h + +#define MD5_DIGEST_LENGTH 16 /* digest length in bytes */ +#define MD5_BLOCK_BYTES 64 /* block size in bytes */ +#define MD5_BLOCK_LONG (MD5_BLOCK_BYTES / sizeof(mDNSu32)) + +typedef struct MD5state_st +{ + mDNSu32 A,B,C,D; + mDNSu32 Nl,Nh; + mDNSu32 data[MD5_BLOCK_LONG]; + int num; +} MD5_CTX; + + +// from openssl/md5.h + +#define MD5_CBLOCK 64 +#define MD5_LBLOCK (MD5_CBLOCK/4) +#define MD5_DIGEST_LENGTH 16 + +int MD5_Init(MD5_CTX *c); +int MD5_Update(MD5_CTX *c, const void *data, unsigned long len); +int MD5_Final(unsigned char *md, MD5_CTX *c); +void MD5_Transform(MD5_CTX *c, const unsigned char *b); + +// From md5_locl.h + +#ifndef MD5_LONG_LOG2 +#define MD5_LONG_LOG2 2 /* default to 32 bits */ +#endif + +#ifdef MD5_ASM +# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__INTEL__) +# define md5_block_host_order md5_block_asm_host_order +# elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC) + void md5_block_asm_data_order_aligned (MD5_CTX *c, const mDNSu32 *p,int num); +# define HASH_BLOCK_DATA_ORDER_ALIGNED md5_block_asm_data_order_aligned +# endif +#endif + +void md5_block_host_order (MD5_CTX *c, const void *p,int num); +void md5_block_data_order (MD5_CTX *c, const void *p,int num); + +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__INTEL__) +/* + * *_block_host_order is expected to handle aligned data while + * *_block_data_order - unaligned. As algorithm and host (x86) + * are in this case of the same "endianness" these two are + * otherwise indistinguishable. But normally you don't want to + * call the same function because unaligned access in places + * where alignment is expected is usually a "Bad Thing". Indeed, + * on RISCs you get punished with BUS ERROR signal or *severe* + * performance degradation. Intel CPUs are in turn perfectly + * capable of loading unaligned data without such drastic side + * effect. Yes, they say it's slower than aligned load, but no + * exception is generated and therefore performance degradation + * is *incomparable* with RISCs. What we should weight here is + * costs of unaligned access against costs of aligning data. + * According to my measurements allowing unaligned access results + * in ~9% performance improvement on Pentium II operating at + * 266MHz. I won't be surprised if the difference will be higher + * on faster systems:-) + * + * <appro@fy.chalmers.se> + */ +#define md5_block_data_order md5_block_host_order +#endif + +#define DATA_ORDER_IS_LITTLE_ENDIAN + +#define HASH_LONG mDNSu32 +#define HASH_LONG_LOG2 MD5_LONG_LOG2 +#define HASH_CTX MD5_CTX +#define HASH_CBLOCK MD5_CBLOCK +#define HASH_LBLOCK MD5_LBLOCK + +#define HASH_UPDATE MD5_Update +#define HASH_TRANSFORM MD5_Transform +#define HASH_FINAL MD5_Final + +#define HASH_MAKE_STRING(c,s) do { \ + unsigned long ll; \ + ll=(c)->A; HOST_l2c(ll,(s)); \ + ll=(c)->B; HOST_l2c(ll,(s)); \ + ll=(c)->C; HOST_l2c(ll,(s)); \ + ll=(c)->D; HOST_l2c(ll,(s)); \ + } while (0) +#define HASH_BLOCK_HOST_ORDER md5_block_host_order +#if !defined(L_ENDIAN) || defined(md5_block_data_order) +#define HASH_BLOCK_DATA_ORDER md5_block_data_order +/* + * Little-endians (Intel and Alpha) feel better without this. + * It looks like memcpy does better job than generic + * md5_block_data_order on copying-n-aligning input data. + * But frankly speaking I didn't expect such result on Alpha. + * On the other hand I've got this with egcs-1.0.2 and if + * program is compiled with another (better?) compiler it + * might turn out other way around. + * + * <appro@fy.chalmers.se> + */ +#endif + + +// from md32_common.h + +/* + * This is a generic 32 bit "collector" for message digest algorithms. + * Whenever needed it collects input character stream into chunks of + * 32 bit values and invokes a block function that performs actual hash + * calculations. + * + * Porting guide. + * + * Obligatory macros: + * + * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN + * this macro defines byte order of input stream. + * HASH_CBLOCK + * size of a unit chunk HASH_BLOCK operates on. + * HASH_LONG + * has to be at lest 32 bit wide, if it's wider, then + * HASH_LONG_LOG2 *has to* be defined along + * HASH_CTX + * context structure that at least contains following + * members: + * typedef struct { + * ... + * HASH_LONG Nl,Nh; + * HASH_LONG data[HASH_LBLOCK]; + * int num; + * ... + * } HASH_CTX; + * HASH_UPDATE + * name of "Update" function, implemented here. + * HASH_TRANSFORM + * name of "Transform" function, implemented here. + * HASH_FINAL + * name of "Final" function, implemented here. + * HASH_BLOCK_HOST_ORDER + * name of "block" function treating *aligned* input message + * in host byte order, implemented externally. + * HASH_BLOCK_DATA_ORDER + * name of "block" function treating *unaligned* input message + * in original (data) byte order, implemented externally (it + * actually is optional if data and host are of the same + * "endianess"). + * HASH_MAKE_STRING + * macro convering context variables to an ASCII hash string. + * + * Optional macros: + * + * B_ENDIAN or L_ENDIAN + * defines host byte-order. + * HASH_LONG_LOG2 + * defaults to 2 if not states otherwise. + * HASH_LBLOCK + * assumed to be HASH_CBLOCK/4 if not stated otherwise. + * HASH_BLOCK_DATA_ORDER_ALIGNED + * alternative "block" function capable of treating + * aligned input message in original (data) order, + * implemented externally. + * + * MD5 example: + * + * #define DATA_ORDER_IS_LITTLE_ENDIAN + * + * #define HASH_LONG mDNSu32 + * #define HASH_LONG_LOG2 mDNSu32_LOG2 + * #define HASH_CTX MD5_CTX + * #define HASH_CBLOCK MD5_CBLOCK + * #define HASH_LBLOCK MD5_LBLOCK + * #define HASH_UPDATE MD5_Update + * #define HASH_TRANSFORM MD5_Transform + * #define HASH_FINAL MD5_Final + * #define HASH_BLOCK_HOST_ORDER md5_block_host_order + * #define HASH_BLOCK_DATA_ORDER md5_block_data_order + * + * <appro@fy.chalmers.se> + */ + +#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) +#error "DATA_ORDER must be defined!" +#endif + +#ifndef HASH_CBLOCK +#error "HASH_CBLOCK must be defined!" +#endif +#ifndef HASH_LONG +#error "HASH_LONG must be defined!" +#endif +#ifndef HASH_CTX +#error "HASH_CTX must be defined!" +#endif + +#ifndef HASH_UPDATE +#error "HASH_UPDATE must be defined!" +#endif +#ifndef HASH_TRANSFORM +#error "HASH_TRANSFORM must be defined!" +#endif +#ifndef HASH_FINAL +#error "HASH_FINAL must be defined!" +#endif + +#ifndef HASH_BLOCK_HOST_ORDER +#error "HASH_BLOCK_HOST_ORDER must be defined!" +#endif + +#if 0 +/* + * Moved below as it's required only if HASH_BLOCK_DATA_ORDER_ALIGNED + * isn't defined. + */ +#ifndef HASH_BLOCK_DATA_ORDER +#error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif +#endif + +#ifndef HASH_LBLOCK +#define HASH_LBLOCK (HASH_CBLOCK/4) +#endif + +#ifndef HASH_LONG_LOG2 +#define HASH_LONG_LOG2 2 +#endif + +/* + * Engage compiler specific rotate intrinsic function if available. + */ +#undef ROTATE +#ifndef PEDANTIC +# if 0 /* defined(_MSC_VER) */ +# define ROTATE(a,n) _lrotl(a,n) +# elif defined(__MWERKS__) +# if defined(__POWERPC__) +# define ROTATE(a,n) (unsigned MD32_REG_T)__rlwinm((int)a,n,0,31) +# elif defined(__MC68K__) + /* Motorola specific tweak. <appro@fy.chalmers.se> */ +# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) ) +# else +# define ROTATE(a,n) __rol(a,n) +# endif +# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) + /* + * Some GNU C inline assembler templates. Note that these are + * rotates by *constant* number of bits! But that's exactly + * what we need here... + * + * <appro@fy.chalmers.se> + */ +# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "roll %1,%0" \ + : "=r"(ret) \ + : "I"(n), "0"(a) \ + : "cc"); \ + ret; \ + }) +# elif defined(__powerpc) || defined(__ppc) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "rlwinm %0,%1,%2,0,31" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# endif +# endif + +/* + * Engage compiler specific "fetch in reverse byte order" + * intrinsic function if available. + */ +# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) + /* some GNU C inline assembler templates by <appro@fy.chalmers.se> */ +# if (defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)) && !defined(I386_ONLY) +# define BE_FETCH32(a) ({ register unsigned int l=(a);\ + asm ( \ + "bswapl %0" \ + : "=r"(l) : "0"(l)); \ + l; \ + }) +# elif defined(__powerpc) +# define LE_FETCH32(a) ({ register unsigned int l; \ + asm ( \ + "lwbrx %0,0,%1" \ + : "=r"(l) \ + : "r"(a)); \ + l; \ + }) + +# elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC) +# define LE_FETCH32(a) ({ register unsigned int l; \ + asm ( \ + "lda [%1]#ASI_PRIMARY_LITTLE,%0"\ + : "=r"(l) \ + : "r"(a)); \ + l; \ + }) +# endif +# endif +#endif /* PEDANTIC */ + +#if HASH_LONG_LOG2==2 /* Engage only if sizeof(HASH_LONG)== 4 */ +/* A nice byte order reversal from Wei Dai <weidai@eskimo.com> */ +#ifdef ROTATE +/* 5 instructions with rotate instruction, else 9 */ +#define REVERSE_FETCH32(a,l) ( \ + l=*(const HASH_LONG *)(a), \ + ((ROTATE(l,8)&0x00FF00FF)|(ROTATE((l&0x00FF00FF),24))) \ + ) +#else +/* 6 instructions with rotate instruction, else 8 */ +#define REVERSE_FETCH32(a,l) ( \ + l=*(const HASH_LONG *)(a), \ + l=(((l>>8)&0x00FF00FF)|((l&0x00FF00FF)<<8)), \ + ROTATE(l,16) \ + ) +/* + * Originally the middle line started with l=(((l&0xFF00FF00)>>8)|... + * It's rewritten as above for two reasons: + * - RISCs aren't good at long constants and have to explicitely + * compose 'em with several (well, usually 2) instructions in a + * register before performing the actual operation and (as you + * already realized:-) having same constant should inspire the + * compiler to permanently allocate the only register for it; + * - most modern CPUs have two ALUs, but usually only one has + * circuitry for shifts:-( this minor tweak inspires compiler + * to schedule shift instructions in a better way... + * + * <appro@fy.chalmers.se> + */ +#endif +#endif + +#ifndef ROTATE +#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +/* + * Make some obvious choices. E.g., HASH_BLOCK_DATA_ORDER_ALIGNED + * and HASH_BLOCK_HOST_ORDER ought to be the same if input data + * and host are of the same "endianess". It's possible to mask + * this with blank #define HASH_BLOCK_DATA_ORDER though... + * + * <appro@fy.chalmers.se> + */ +#if defined(B_ENDIAN) +# if defined(DATA_ORDER_IS_BIG_ENDIAN) +# if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2 +# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER +# endif +# elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) +# ifndef HOST_FETCH32 +# ifdef LE_FETCH32 +# define HOST_FETCH32(p,l) LE_FETCH32(p) +# elif defined(REVERSE_FETCH32) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# endif +# endif +# endif +#elif defined(L_ENDIAN) +# if defined(DATA_ORDER_IS_LITTLE_ENDIAN) +# if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2 +# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER +# endif +# elif defined(DATA_ORDER_IS_BIG_ENDIAN) +# ifndef HOST_FETCH32 +# ifdef BE_FETCH32 +# define HOST_FETCH32(p,l) BE_FETCH32(p) +# elif defined(REVERSE_FETCH32) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# endif +# endif +# endif +#endif + +#if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) +#ifndef HASH_BLOCK_DATA_ORDER +#error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif +#endif + +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + +#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++))) ), \ + l) +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + case 3: l|=((unsigned long)(*((c)++))); \ + } } +#define HOST_p_c2l_p(c,l,sc,len) { \ + switch (sc) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + } } +/* NOTE the pointer is not incremented at the end of this */ +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<< 8; \ + case 2: l|=((unsigned long)(*(--(c))))<<16; \ + case 1: l|=((unsigned long)(*(--(c))))<<24; \ + } } +#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff), \ + l) + +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + +#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24), \ + l) +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++))); \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + case 3: l|=((unsigned long)(*((c)++)))<<24; \ + } } +#define HOST_p_c2l_p(c,l,sc,len) { \ + switch (sc) { \ + case 0: l =((unsigned long)(*((c)++))); \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + } } +/* NOTE the pointer is not incremented at the end of this */ +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<<16; \ + case 2: l|=((unsigned long)(*(--(c))))<< 8; \ + case 1: l|=((unsigned long)(*(--(c)))); \ + } } +#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff), \ + l) + +#endif + +/* + * Time for some action:-) + */ + +int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) + { + const unsigned char *data=(const unsigned char *)data_; + register HASH_LONG * p; + register unsigned long l; + int sw,sc,ew,ec; + + if (len==0) return 1; + + l=(c->Nl+(len<<3))&0xffffffffL; + /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to + * Wei Dai <weidai@eskimo.com> for pointing it out. */ + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh+=(len>>29); + c->Nl=l; + + if (c->num != 0) + { + p=c->data; + sw=c->num>>2; + sc=c->num&0x03; + + if ((c->num+len) >= HASH_CBLOCK) + { + l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l; + for (; sw<HASH_LBLOCK; sw++) + { + HOST_c2l(data,l); p[sw]=l; + } + HASH_BLOCK_HOST_ORDER (c,p,1); + len-=(HASH_CBLOCK-c->num); + c->num=0; + /* drop through and do the rest */ + } + else + { + c->num+=len; + if ((sc+len) < 4) /* ugly, add char's to a word */ + { + l=p[sw]; HOST_p_c2l_p(data,l,sc,len); p[sw]=l; + } + else + { + ew=(c->num>>2); + ec=(c->num&0x03); + if (sc) + l=p[sw]; + HOST_p_c2l(data,l,sc); + p[sw++]=l; + for (; sw < ew; sw++) + { + HOST_c2l(data,l); p[sw]=l; + } + if (ec) + { + HOST_c2l_p(data,l,ec); p[sw]=l; + } + } + return 1; + } + } + + sw=(int)(len/HASH_CBLOCK); + if (sw > 0) + { +#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED) + /* + * Note that HASH_BLOCK_DATA_ORDER_ALIGNED gets defined + * only if sizeof(HASH_LONG)==4. + */ + if ((((unsigned long)data)%4) == 0) + { + /* data is properly aligned so that we can cast it: */ + HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,sw); + sw*=HASH_CBLOCK; + data+=sw; + len-=sw; + } + else +#if !defined(HASH_BLOCK_DATA_ORDER) + while (sw--) + { + memcpy (p=c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED(c,p,1); + data+=HASH_CBLOCK; + len-=HASH_CBLOCK; + } +#endif +#endif +#if defined(HASH_BLOCK_DATA_ORDER) + { + HASH_BLOCK_DATA_ORDER(c,data,sw); + sw*=HASH_CBLOCK; + data+=sw; + len-=sw; + } +#endif + } + + if (len!=0) + { + p = c->data; + c->num = (int)len; + ew=(int)(len>>2); /* words to copy */ + ec=(int)(len&0x03); + for (; ew; ew--,p++) + { + HOST_c2l(data,l); *p=l; + } + HOST_c2l_p(data,l,ec); + *p=l; + } + return 1; + } + + +void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data) + { +#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED) + if ((((unsigned long)data)%4) == 0) + /* data is properly aligned so that we can cast it: */ + HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,1); + else +#if !defined(HASH_BLOCK_DATA_ORDER) + { + memcpy (c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED (c,c->data,1); + } +#endif +#endif +#if defined(HASH_BLOCK_DATA_ORDER) + HASH_BLOCK_DATA_ORDER (c,data,1); +#endif + } + + +int HASH_FINAL (unsigned char *md, HASH_CTX *c) + { + register HASH_LONG *p; + register unsigned long l; + register int i,j; + static const unsigned char end[4]={0x80,0x00,0x00,0x00}; + const unsigned char *cp=end; + + /* c->num should definitly have room for at least one more byte. */ + p=c->data; + i=c->num>>2; + j=c->num&0x03; + +#if 0 + /* purify often complains about the following line as an + * Uninitialized Memory Read. While this can be true, the + * following p_c2l macro will reset l when that case is true. + * This is because j&0x03 contains the number of 'valid' bytes + * already in p[i]. If and only if j&0x03 == 0, the UMR will + * occur but this is also the only time p_c2l will do + * l= *(cp++) instead of l|= *(cp++) + * Many thanks to Alex Tang <altitude@cic.net> for pickup this + * 'potential bug' */ +#ifdef PURIFY + if (j==0) p[i]=0; /* Yeah, but that's not the way to fix it:-) */ +#endif + l=p[i]; +#else + l = (j==0) ? 0 : p[i]; +#endif + HOST_p_c2l(cp,l,j); p[i++]=l; /* i is the next 'undefined word' */ + + if (i>(HASH_LBLOCK-2)) /* save room for Nl and Nh */ + { + if (i<HASH_LBLOCK) p[i]=0; + HASH_BLOCK_HOST_ORDER (c,p,1); + i=0; + } + for (; i<(HASH_LBLOCK-2); i++) + p[i]=0; + +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + p[HASH_LBLOCK-2]=c->Nh; + p[HASH_LBLOCK-1]=c->Nl; +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + p[HASH_LBLOCK-2]=c->Nl; + p[HASH_LBLOCK-1]=c->Nh; +#endif + HASH_BLOCK_HOST_ORDER (c,p,1); + +#ifndef HASH_MAKE_STRING +#error "HASH_MAKE_STRING must be defined!" +#else + HASH_MAKE_STRING(c,md); +#endif + + c->num=0; + /* clear stuff, HASH_BLOCK may be leaving some stuff on the stack + * but I'm not worried :-) + OPENSSL_cleanse((void *)c,sizeof(HASH_CTX)); + */ + return 1; + } + +#ifndef MD32_REG_T +#define MD32_REG_T long +/* + * This comment was originaly written for MD5, which is why it + * discusses A-D. But it basically applies to all 32-bit digests, + * which is why it was moved to common header file. + * + * In case you wonder why A-D are declared as long and not + * as mDNSu32. Doing so results in slight performance + * boost on LP64 architectures. The catch is we don't + * really care if 32 MSBs of a 64-bit register get polluted + * with eventual overflows as we *save* only 32 LSBs in + * *either* case. Now declaring 'em long excuses the compiler + * from keeping 32 MSBs zeroed resulting in 13% performance + * improvement under SPARC Solaris7/64 and 5% under AlphaLinux. + * Well, to be honest it should say that this *prevents* + * performance degradation. + * <appro@fy.chalmers.se> + * Apparently there're LP64 compilers that generate better + * code if A-D are declared int. Most notably GCC-x86_64 + * generates better code. + * <appro@fy.chalmers.se> + */ +#endif + + +// from md5_locl.h (continued) + +/* +#define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) +#define G(x,y,z) (((x) & (z)) | ((y) & (~(z)))) +*/ + +/* As pointed out by Wei Dai <weidai@eskimo.com>, the above can be + * simplified to the code below. Wei attributes these optimizations + * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel. + */ +#define F(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) +#define G(b,c,d) ((((b) ^ (c)) & (d)) ^ (c)) +#define H(b,c,d) ((b) ^ (c) ^ (d)) +#define I(b,c,d) (((~(d)) | (b)) ^ (c)) + +#define R0(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+F((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; };\ + +#define R1(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+G((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + +#define R2(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+H((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + +#define R3(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+I((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + +// from md5_dgst.c + + +/* Implemented from RFC1321 The MD5 Message-Digest Algorithm + */ + +#define INIT_DATA_A (unsigned long)0x67452301L +#define INIT_DATA_B (unsigned long)0xefcdab89L +#define INIT_DATA_C (unsigned long)0x98badcfeL +#define INIT_DATA_D (unsigned long)0x10325476L + +int MD5_Init(MD5_CTX *c) + { + c->A=INIT_DATA_A; + c->B=INIT_DATA_B; + c->C=INIT_DATA_C; + c->D=INIT_DATA_D; + c->Nl=0; + c->Nh=0; + c->num=0; + return 1; + } + +#ifndef md5_block_host_order +void md5_block_host_order (MD5_CTX *c, const void *data, int num) + { + const mDNSu32 *X=(const mDNSu32 *)data; + register unsigned MD32_REG_T A,B,C,D; + + A=c->A; + B=c->B; + C=c->C; + D=c->D; + + for (;num--;X+=HASH_LBLOCK) + { + /* Round 0 */ + R0(A,B,C,D,X[ 0], 7,0xd76aa478L); + R0(D,A,B,C,X[ 1],12,0xe8c7b756L); + R0(C,D,A,B,X[ 2],17,0x242070dbL); + R0(B,C,D,A,X[ 3],22,0xc1bdceeeL); + R0(A,B,C,D,X[ 4], 7,0xf57c0fafL); + R0(D,A,B,C,X[ 5],12,0x4787c62aL); + R0(C,D,A,B,X[ 6],17,0xa8304613L); + R0(B,C,D,A,X[ 7],22,0xfd469501L); + R0(A,B,C,D,X[ 8], 7,0x698098d8L); + R0(D,A,B,C,X[ 9],12,0x8b44f7afL); + R0(C,D,A,B,X[10],17,0xffff5bb1L); + R0(B,C,D,A,X[11],22,0x895cd7beL); + R0(A,B,C,D,X[12], 7,0x6b901122L); + R0(D,A,B,C,X[13],12,0xfd987193L); + R0(C,D,A,B,X[14],17,0xa679438eL); + R0(B,C,D,A,X[15],22,0x49b40821L); + /* Round 1 */ + R1(A,B,C,D,X[ 1], 5,0xf61e2562L); + R1(D,A,B,C,X[ 6], 9,0xc040b340L); + R1(C,D,A,B,X[11],14,0x265e5a51L); + R1(B,C,D,A,X[ 0],20,0xe9b6c7aaL); + R1(A,B,C,D,X[ 5], 5,0xd62f105dL); + R1(D,A,B,C,X[10], 9,0x02441453L); + R1(C,D,A,B,X[15],14,0xd8a1e681L); + R1(B,C,D,A,X[ 4],20,0xe7d3fbc8L); + R1(A,B,C,D,X[ 9], 5,0x21e1cde6L); + R1(D,A,B,C,X[14], 9,0xc33707d6L); + R1(C,D,A,B,X[ 3],14,0xf4d50d87L); + R1(B,C,D,A,X[ 8],20,0x455a14edL); + R1(A,B,C,D,X[13], 5,0xa9e3e905L); + R1(D,A,B,C,X[ 2], 9,0xfcefa3f8L); + R1(C,D,A,B,X[ 7],14,0x676f02d9L); + R1(B,C,D,A,X[12],20,0x8d2a4c8aL); + /* Round 2 */ + R2(A,B,C,D,X[ 5], 4,0xfffa3942L); + R2(D,A,B,C,X[ 8],11,0x8771f681L); + R2(C,D,A,B,X[11],16,0x6d9d6122L); + R2(B,C,D,A,X[14],23,0xfde5380cL); + R2(A,B,C,D,X[ 1], 4,0xa4beea44L); + R2(D,A,B,C,X[ 4],11,0x4bdecfa9L); + R2(C,D,A,B,X[ 7],16,0xf6bb4b60L); + R2(B,C,D,A,X[10],23,0xbebfbc70L); + R2(A,B,C,D,X[13], 4,0x289b7ec6L); + R2(D,A,B,C,X[ 0],11,0xeaa127faL); + R2(C,D,A,B,X[ 3],16,0xd4ef3085L); + R2(B,C,D,A,X[ 6],23,0x04881d05L); + R2(A,B,C,D,X[ 9], 4,0xd9d4d039L); + R2(D,A,B,C,X[12],11,0xe6db99e5L); + R2(C,D,A,B,X[15],16,0x1fa27cf8L); + R2(B,C,D,A,X[ 2],23,0xc4ac5665L); + /* Round 3 */ + R3(A,B,C,D,X[ 0], 6,0xf4292244L); + R3(D,A,B,C,X[ 7],10,0x432aff97L); + R3(C,D,A,B,X[14],15,0xab9423a7L); + R3(B,C,D,A,X[ 5],21,0xfc93a039L); + R3(A,B,C,D,X[12], 6,0x655b59c3L); + R3(D,A,B,C,X[ 3],10,0x8f0ccc92L); + R3(C,D,A,B,X[10],15,0xffeff47dL); + R3(B,C,D,A,X[ 1],21,0x85845dd1L); + R3(A,B,C,D,X[ 8], 6,0x6fa87e4fL); + R3(D,A,B,C,X[15],10,0xfe2ce6e0L); + R3(C,D,A,B,X[ 6],15,0xa3014314L); + R3(B,C,D,A,X[13],21,0x4e0811a1L); + R3(A,B,C,D,X[ 4], 6,0xf7537e82L); + R3(D,A,B,C,X[11],10,0xbd3af235L); + R3(C,D,A,B,X[ 2],15,0x2ad7d2bbL); + R3(B,C,D,A,X[ 9],21,0xeb86d391L); + + A = c->A += A; + B = c->B += B; + C = c->C += C; + D = c->D += D; + } + } +#endif + +#ifndef md5_block_data_order +#ifdef X +#undef X +#endif +void md5_block_data_order (MD5_CTX *c, const void *data_, int num) + { + const unsigned char *data=data_; + register unsigned MD32_REG_T A,B,C,D,l; +#ifndef MD32_XARRAY + /* See comment in crypto/sha/sha_locl.h for details. */ + unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, + XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15; +# define X(i) XX##i +#else + mDNSu32 XX[MD5_LBLOCK]; +# define X(i) XX[i] +#endif + + A=c->A; + B=c->B; + C=c->C; + D=c->D; + + for (;num--;) + { + HOST_c2l(data,l); X( 0)=l; HOST_c2l(data,l); X( 1)=l; + /* Round 0 */ + R0(A,B,C,D,X( 0), 7,0xd76aa478L); HOST_c2l(data,l); X( 2)=l; + R0(D,A,B,C,X( 1),12,0xe8c7b756L); HOST_c2l(data,l); X( 3)=l; + R0(C,D,A,B,X( 2),17,0x242070dbL); HOST_c2l(data,l); X( 4)=l; + R0(B,C,D,A,X( 3),22,0xc1bdceeeL); HOST_c2l(data,l); X( 5)=l; + R0(A,B,C,D,X( 4), 7,0xf57c0fafL); HOST_c2l(data,l); X( 6)=l; + R0(D,A,B,C,X( 5),12,0x4787c62aL); HOST_c2l(data,l); X( 7)=l; + R0(C,D,A,B,X( 6),17,0xa8304613L); HOST_c2l(data,l); X( 8)=l; + R0(B,C,D,A,X( 7),22,0xfd469501L); HOST_c2l(data,l); X( 9)=l; + R0(A,B,C,D,X( 8), 7,0x698098d8L); HOST_c2l(data,l); X(10)=l; + R0(D,A,B,C,X( 9),12,0x8b44f7afL); HOST_c2l(data,l); X(11)=l; + R0(C,D,A,B,X(10),17,0xffff5bb1L); HOST_c2l(data,l); X(12)=l; + R0(B,C,D,A,X(11),22,0x895cd7beL); HOST_c2l(data,l); X(13)=l; + R0(A,B,C,D,X(12), 7,0x6b901122L); HOST_c2l(data,l); X(14)=l; + R0(D,A,B,C,X(13),12,0xfd987193L); HOST_c2l(data,l); X(15)=l; + R0(C,D,A,B,X(14),17,0xa679438eL); + R0(B,C,D,A,X(15),22,0x49b40821L); + /* Round 1 */ + R1(A,B,C,D,X( 1), 5,0xf61e2562L); + R1(D,A,B,C,X( 6), 9,0xc040b340L); + R1(C,D,A,B,X(11),14,0x265e5a51L); + R1(B,C,D,A,X( 0),20,0xe9b6c7aaL); + R1(A,B,C,D,X( 5), 5,0xd62f105dL); + R1(D,A,B,C,X(10), 9,0x02441453L); + R1(C,D,A,B,X(15),14,0xd8a1e681L); + R1(B,C,D,A,X( 4),20,0xe7d3fbc8L); + R1(A,B,C,D,X( 9), 5,0x21e1cde6L); + R1(D,A,B,C,X(14), 9,0xc33707d6L); + R1(C,D,A,B,X( 3),14,0xf4d50d87L); + R1(B,C,D,A,X( 8),20,0x455a14edL); + R1(A,B,C,D,X(13), 5,0xa9e3e905L); + R1(D,A,B,C,X( 2), 9,0xfcefa3f8L); + R1(C,D,A,B,X( 7),14,0x676f02d9L); + R1(B,C,D,A,X(12),20,0x8d2a4c8aL); + /* Round 2 */ + R2(A,B,C,D,X( 5), 4,0xfffa3942L); + R2(D,A,B,C,X( 8),11,0x8771f681L); + R2(C,D,A,B,X(11),16,0x6d9d6122L); + R2(B,C,D,A,X(14),23,0xfde5380cL); + R2(A,B,C,D,X( 1), 4,0xa4beea44L); + R2(D,A,B,C,X( 4),11,0x4bdecfa9L); + R2(C,D,A,B,X( 7),16,0xf6bb4b60L); + R2(B,C,D,A,X(10),23,0xbebfbc70L); + R2(A,B,C,D,X(13), 4,0x289b7ec6L); + R2(D,A,B,C,X( 0),11,0xeaa127faL); + R2(C,D,A,B,X( 3),16,0xd4ef3085L); + R2(B,C,D,A,X( 6),23,0x04881d05L); + R2(A,B,C,D,X( 9), 4,0xd9d4d039L); + R2(D,A,B,C,X(12),11,0xe6db99e5L); + R2(C,D,A,B,X(15),16,0x1fa27cf8L); + R2(B,C,D,A,X( 2),23,0xc4ac5665L); + /* Round 3 */ + R3(A,B,C,D,X( 0), 6,0xf4292244L); + R3(D,A,B,C,X( 7),10,0x432aff97L); + R3(C,D,A,B,X(14),15,0xab9423a7L); + R3(B,C,D,A,X( 5),21,0xfc93a039L); + R3(A,B,C,D,X(12), 6,0x655b59c3L); + R3(D,A,B,C,X( 3),10,0x8f0ccc92L); + R3(C,D,A,B,X(10),15,0xffeff47dL); + R3(B,C,D,A,X( 1),21,0x85845dd1L); + R3(A,B,C,D,X( 8), 6,0x6fa87e4fL); + R3(D,A,B,C,X(15),10,0xfe2ce6e0L); + R3(C,D,A,B,X( 6),15,0xa3014314L); + R3(B,C,D,A,X(13),21,0x4e0811a1L); + R3(A,B,C,D,X( 4), 6,0xf7537e82L); + R3(D,A,B,C,X(11),10,0xbd3af235L); + R3(C,D,A,B,X( 2),15,0x2ad7d2bbL); + R3(B,C,D,A,X( 9),21,0xeb86d391L); + + A = c->A += A; + B = c->B += B; + C = c->C += C; + D = c->D += D; + } + } +#endif + + + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - base64 -> binary conversion +#endif + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + + +#define mDNSisspace(x) (x == '\t' || x == '\n' || x == '\v' || x == '\f' || x == '\r' || x == ' ') + +static const char *mDNSstrchr(const char *s, int c) + { + while (1) + { + if (c == *s) return s; + if (!*s) return mDNSNULL; + s++; + } + } + +// skips all whitespace anywhere. +// converts characters, four at a time, starting at (or after) +// src from base - 64 numbers into three 8 bit bytes in the target area. +// it returns the number of data bytes stored at the target, or -1 on error. +// adapted from BIND sources + +mDNSexport mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize) + { + int tarindex, state, ch; + const char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (mDNSisspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = mDNSstrchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((mDNSu32)tarindex >= targsize) + return (-1); + target[tarindex] = (mDNSu8)((pos - Base64) << 2); + } + state = 1; + break; + case 1: + if (target) { + if ((mDNSu32)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x0f) << 4); + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((mDNSu32)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x03) << 6); + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((mDNSu32)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + return -1; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)mDNSNULL; ch != '\0'; ch = *src++) + if (!mDNSisspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)mDNSNULL; ch != '\0'; ch = *src++) + if (!mDNSisspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); + } + + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - API exported to mDNS Core +#endif + +// Constants +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + +#define HMAC_MD5_AlgName (*(const domainname*) "\010" "hmac-md5" "\007" "sig-alg" "\003" "reg" "\003" "int") + +// Adapted from Appendix, RFC 2104 +mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, const mDNSu8 *key, mDNSu32 len) + { + MD5_CTX k; + mDNSu8 buf[MD5_LEN]; + int i; + + // If key is longer than HMAC_LEN reset it to MD5(key) + if (len > HMAC_LEN) + { + MD5_Init(&k); + MD5_Update(&k, key, len); + MD5_Final(buf, &k); + key = buf; + len = MD5_LEN; + } + + // store key in pads + mDNSPlatformMemZero(info->key.ipad, HMAC_LEN); + mDNSPlatformMemZero(info->key.opad, HMAC_LEN); + mDNSPlatformMemCopy(key, info->key.ipad, len); + mDNSPlatformMemCopy(key, info->key.opad, len); + + // XOR key with ipad and opad values + for (i = 0; i < HMAC_LEN; i++) + { + info->key.ipad[i] ^= HMAC_IPAD; + info->key.opad[i] ^= HMAC_OPAD; + } + + } + +mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info) + { + AuthRecord tsig; + mDNSu8 *countPtr, *rdata; + mDNSu32 utc32; + mDNSu8 utc48[6]; + mDNSu8 digest[MD5_LEN]; + mDNSu8 *ptr = *end; + mDNSu32 len; + mDNSOpaque16 buf; + MD5_CTX c; + + // Init MD5 context, digest inner key pad and message + MD5_Init(&c); + MD5_Update(&c, info->key.ipad, HMAC_LEN); + MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg)); + + // Construct TSIG RR, digesting variables as apporpriate + mDNSPlatformMemZero(&tsig, sizeof(AuthRecord)); + mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + + // key name + AssignDomainName(tsig.resrec.name, &info->keyname); + MD5_Update(&c, info->keyname.c, DomainNameLength(&info->keyname)); + + // class + tsig.resrec.rrclass = kDNSQClass_ANY; + buf = mDNSOpaque16fromIntVal(kDNSQClass_ANY); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + + // ttl + tsig.resrec.rroriginalttl = 0; + MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl)); + + // alg name + AssignDomainName(&tsig.resrec.rdata->u.name, &HMAC_MD5_AlgName); + len = DomainNameLength(&HMAC_MD5_AlgName); + rdata = tsig.resrec.rdata->u.data + len; + MD5_Update(&c, HMAC_MD5_AlgName.c, len); + + // time + // get UTC (universal time), convert to 48-bit unsigned in network byte order + utc32 = (mDNSu32)mDNSPlatformUTC(); + if (utc32 == (unsigned)-1) { LogMsg("ERROR: DNSDigest_SignMessage - mDNSPlatformUTC returned bad time -1"); return mDNSNULL; } + utc48[0] = 0; + utc48[1] = 0; + utc48[2] = (mDNSu8)((utc32 >> 24) & 0xff); + utc48[3] = (mDNSu8)((utc32 >> 16) & 0xff); + utc48[4] = (mDNSu8)((utc32 >> 8) & 0xff); + utc48[5] = (mDNSu8)( utc32 & 0xff); + + mDNSPlatformMemCopy(utc48, rdata, 6); + rdata += 6; + MD5_Update(&c, utc48, 6); + + // 300 sec is fudge recommended in RFC 2485 + rdata[0] = (mDNSu8)((300 >> 8) & 0xff); + rdata[1] = (mDNSu8)( 300 & 0xff); + MD5_Update(&c, rdata, sizeof(mDNSOpaque16)); + rdata += sizeof(mDNSOpaque16); + + // digest error and other data len (both zero) - we'll add them to the rdata later + buf.NotAnInteger = 0; + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // error + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // other data len + + // finish the message & tsig var hash + MD5_Final(digest, &c); + + // perform outer MD5 (outer key pad, inner digest) + MD5_Init(&c); + MD5_Update(&c, info->key.opad, HMAC_LEN); + MD5_Update(&c, digest, MD5_LEN); + MD5_Final(digest, &c); + + // set remaining rdata fields + rdata[0] = (mDNSu8)((MD5_LEN >> 8) & 0xff); + rdata[1] = (mDNSu8)( MD5_LEN & 0xff); + rdata += sizeof(mDNSOpaque16); + mDNSPlatformMemCopy(digest, rdata, MD5_LEN); // MAC + rdata += MD5_LEN; + rdata[0] = msg->h.id.b[0]; // original ID + rdata[1] = msg->h.id.b[1]; + rdata[2] = 0; // no error + rdata[3] = 0; + rdata[4] = 0; // other data len + rdata[5] = 0; + rdata += 6; + + tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data); + *end = PutResourceRecordTTLJumbo(msg, ptr, numAdditionals, &tsig.resrec, 0); + if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); return mDNSNULL; } + + // update num additionals + countPtr = (mDNSu8 *)&msg->h.numAdditionals; // increment (network-byte ordered) header value + *countPtr++ = (mDNSu8)(*numAdditionals >> 8); + *countPtr++ = (mDNSu8)(*numAdditionals & 0xFF); + + return *end; + } + +#ifdef __cplusplus +} +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c new file mode 100644 index 0000000000..141c3bc9ca --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c @@ -0,0 +1,334 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + File: GenLinkedList.c + + Contains: implementation of generic linked lists. + + Version: 1.0 + Tabs: 4 spaces + + Change History (most recent first): + +$Log: GenLinkedList.c,v $ +Revision 1.4 2006/08/14 23:24:56 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.3 2004/04/22 21:14:42 cheshire +Fix comment spelling mistake + +Revision 1.2 2004/02/05 07:41:08 cheshire +Add Log header + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "GenLinkedList.h" + + +// Return the link pointer contained within element e at offset o. +#define GETLINK( e, o) ( *(void**)((char*) (e) + (o)) ) + +// Assign the link pointer l to element e at offset o. +#define ASSIGNLINK( e, l, o) ( *((void**)((char*) (e) + (o))) = (l)) + + +// GenLinkedList ///////////////////////////////////////////////////////////// + +void InitLinkedList( GenLinkedList *pList, size_t linkOffset) +/* Initialize the block of memory pointed to by pList as a linked list. */ +{ + pList->Head = NULL; + pList->Tail = NULL; + pList->LinkOffset = linkOffset; +} + + +void AddToTail( GenLinkedList *pList, void *elem) +/* Add a linked list element to the tail of the list. */ +{ + if ( pList->Tail) { + ASSIGNLINK( pList->Tail, elem, pList->LinkOffset); + } else + pList->Head = elem; + ASSIGNLINK( elem, NULL, pList->LinkOffset); + + pList->Tail = elem; +} + + +void AddToHead( GenLinkedList *pList, void *elem) +/* Add a linked list element to the head of the list. */ +{ + ASSIGNLINK( elem, pList->Head, pList->LinkOffset); + if ( pList->Tail == NULL) + pList->Tail = elem; + + pList->Head = elem; +} + + +int RemoveFromList( GenLinkedList *pList, void *elem) +/* Remove a linked list element from the list. Return 0 if it was not found. */ +/* If the element is removed, its link will be set to NULL. */ +{ +void *iElem, *lastElem; + + for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) { + if ( iElem == elem) { + if ( lastElem) { // somewhere past the head + ASSIGNLINK( lastElem, GETLINK( elem, pList->LinkOffset), pList->LinkOffset); + } else { // at the head + pList->Head = GETLINK( elem, pList->LinkOffset); + } + if ( pList->Tail == elem) + pList->Tail = lastElem ? lastElem : NULL; + ASSIGNLINK( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug. + return 1; + } + lastElem = iElem; + } + + return 0; +} + + +int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem) +/* Replace an element in the list with a new element, in the same position. */ +{ +void *iElem, *lastElem; + + if ( elemInList == NULL || newElem == NULL) + return 0; + + for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) + { + if ( iElem == elemInList) + { + ASSIGNLINK( newElem, GETLINK( elemInList, pList->LinkOffset), pList->LinkOffset); + if ( lastElem) // somewhere past the head + { + ASSIGNLINK( lastElem, newElem, pList->LinkOffset); + } + else // at the head + { + pList->Head = newElem; + } + if ( pList->Tail == elemInList) + pList->Tail = newElem; + return 1; + } + lastElem = iElem; + } + + return 0; +} + + +// GenDoubleLinkedList ///////////////////////////////////////////////////////// + +void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, + size_t backLinkOffset) +/* Initialize the block of memory pointed to by pList as a double linked list. */ +{ + pList->Head = NULL; + pList->Tail = NULL; + pList->FwdLinkOffset = fwdLinkOffset; + pList->BackLinkOffset = backLinkOffset; +} + + +void DLLAddToHead( GenDoubleLinkedList *pList, void *elem) +/* Add a linked list element to the head of the list. */ +{ +void *pNext; + + pNext = pList->Head; + + // fix up the forward links + ASSIGNLINK( elem, pList->Head, pList->FwdLinkOffset); + pList->Head = elem; + + // fix up the backward links + if ( pNext) { + ASSIGNLINK( pNext, elem, pList->BackLinkOffset); + } else + pList->Tail = elem; + ASSIGNLINK( elem, NULL, pList->BackLinkOffset); +} + + +void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem) +/* Remove a linked list element from the list. */ +/* When the element is removed, its link will be set to NULL. */ +{ +void *pNext, *pPrev; + + pNext = GETLINK( elem, pList->FwdLinkOffset); + pPrev = GETLINK( elem, pList->BackLinkOffset); + + // fix up the forward links + if ( pPrev) + ASSIGNLINK( pPrev, pNext, pList->FwdLinkOffset); + else + pList->Head = pNext; + + // fix up the backward links + if ( pNext) + ASSIGNLINK( pNext, pPrev, pList->BackLinkOffset); + else + pList->Tail = pPrev; + + ASSIGNLINK( elem, NULL, pList->FwdLinkOffset); + ASSIGNLINK( elem, NULL, pList->BackLinkOffset); +} + + +// GenLinkedOffsetList ///////////////////////////////////////////////////// + +// Extract the Next offset from element +#define GETOFFSET( e, o) ( *(size_t*)((char*) (e) + (o)) ) + +static void AssignOffsetLink( void *elem, void *link, size_t linkOffset); + + +static void AssignOffsetLink( void *elem, void *link, size_t linkOffset) +// Assign link to elem as an offset from elem. Assign 0 to elem if link is NULL. +{ + GETOFFSET( elem, linkOffset) = link ? (size_t) link - (size_t) elem : 0; +} + + +void *GetHeadPtr( GenLinkedOffsetList *pList) +/* Return a pointer to the head element of a list, or NULL if none. */ +{ + return pList->Head ? ( (char*) (pList) + pList->Head) : NULL; +} + + +void *GetTailPtr( GenLinkedOffsetList *pList) +/* Return a pointer to the tail element of a list, or NULL if none. */ +{ + return pList->Tail ? ( (char*) (pList) + pList->Tail) : NULL; +} + + +void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem) +/* Return the link pointer contained within element e for pList, or NULL if it is 0. */ +{ +size_t nextOffset; + + nextOffset = GETOFFSET( elem, pList->LinkOffset); + + return nextOffset ? (char*) elem + nextOffset : NULL; +} + + +void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset) +/* Initialize the block of memory pointed to by pList as a linked list. */ +{ + pList->Head = 0; + pList->Tail = 0; + pList->LinkOffset = linkOffset; +} + + +void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem) +/* Add a linked list element to the tail of the list. */ +{ + if ( pList->Tail) { + AssignOffsetLink( GetTailPtr( pList), elem, pList->LinkOffset); + } else + pList->Head = (size_t) elem - (size_t) pList; + AssignOffsetLink( elem, NULL, pList->LinkOffset); + + pList->Tail = (size_t) elem - (size_t) pList; +} + + +void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem) +/* Add a linked list element to the head of the list. */ +{ + AssignOffsetLink( elem, GetHeadPtr( pList), pList->LinkOffset); + if ( pList->Tail == 0) + pList->Tail = (size_t) elem - (size_t) pList; + + pList->Head = (size_t) elem - (size_t) pList; +} + + +int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem) +/* Remove a linked list element from the list. Return 0 if it was not found. */ +/* If the element is removed, its link will be set to NULL. */ +{ +void *iElem, *lastElem; + + for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; + iElem = GetOffsetLink( pList, iElem)) + { + if ( iElem == elem) { + if ( lastElem) { // somewhere past the head + AssignOffsetLink( lastElem, GetOffsetLink( pList, elem), pList->LinkOffset); + } else { // at the head + iElem = GetOffsetLink( pList, elem); + pList->Head = iElem ? (size_t) iElem - (size_t) pList : 0; + } + if ( GetTailPtr( pList) == elem) + pList->Tail = lastElem ? (size_t) lastElem - (size_t) pList : 0; + AssignOffsetLink( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug. + return 1; + } + lastElem = iElem; + } + + return 0; +} + + +int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem) +/* Replace an element in the list with a new element, in the same position. */ +{ +void *iElem, *lastElem; + + if ( elemInList == NULL || newElem == NULL) + return 0; + + for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; + iElem = GetOffsetLink( pList, iElem)) + { + if ( iElem == elemInList) + { + AssignOffsetLink( newElem, GetOffsetLink( pList, elemInList), pList->LinkOffset); + if ( lastElem) // somewhere past the head + { + AssignOffsetLink( lastElem, newElem, pList->LinkOffset); + } + else // at the head + { + pList->Head = (size_t) newElem - (size_t) pList; + } + if ( GetTailPtr( pList) == elemInList) + pList->Tail = (size_t) newElem - (size_t) pList; + return 1; + } + lastElem = iElem; + } + + return 0; +} + + diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h new file mode 100644 index 0000000000..71863346b0 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h @@ -0,0 +1,109 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + File: GenLinkedList.c + + Contains: interface to generic linked lists. + + Version: 1.0 + Tabs: 4 spaces + + Change History (most recent first): + +$Log: GenLinkedList.h,v $ +Revision 1.3 2006/08/14 23:24:56 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/02/05 07:41:08 cheshire +Add Log header + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __GenLinkedList__ +#define __GenLinkedList__ + + +#include <stddef.h> + + +struct GenLinkedList +{ + void *Head, + *Tail; + size_t LinkOffset; +}; +typedef struct GenLinkedList GenLinkedList; + + +void InitLinkedList( GenLinkedList *pList, size_t linkOffset); + +void AddToHead( GenLinkedList *pList, void *elem); +void AddToTail( GenLinkedList *pList, void *elem); + +int RemoveFromList( GenLinkedList *pList, void *elem); + +int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem); + + + +struct GenDoubleLinkedList +{ + void *Head, + *Tail; + size_t FwdLinkOffset, + BackLinkOffset; +}; +typedef struct GenDoubleLinkedList GenDoubleLinkedList; + + +void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, + size_t backLinkOffset); + +void DLLAddToHead( GenDoubleLinkedList *pList, void *elem); + +void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem); + + + +/* A GenLinkedOffsetList is like a GenLinkedList that stores the *Next field as a signed */ +/* offset from the address of the beginning of the element, rather than as a pointer. */ + +struct GenLinkedOffsetList +{ + size_t Head, + Tail; + size_t LinkOffset; +}; +typedef struct GenLinkedOffsetList GenLinkedOffsetList; + + +void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset); + +void *GetHeadPtr( GenLinkedOffsetList *pList); +void *GetTailPtr( GenLinkedOffsetList *pList); +void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem); + +void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem); +void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem); + +int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem); + +int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem); + + +#endif // __GenLinkedList__ diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile new file mode 100644 index 0000000000..294f9a4622 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile @@ -0,0 +1,83 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +PROG= mdnsd +MANIFEST= multicast.xml + +CMN_DIR= $(SRC)/lib/libdns_sd/common +CMN_OBJS= dnssd_ipc.o +CMN_SRCS= $(CMN_OBJS:%.o=$(CMN_DIR)/%.c) + +LOCAL_OBJS= DNSCommon.o DNSDigest.o GenLinkedList.o \ + PlatformCommon.o PosixDaemon.o \ + mDNS.o mDNSDebug.o mDNSPosix.o mDNSUNP.o \ + uDNS.o uds_daemon.o +LOCAL_SRCS= $(LOCAL_OBJS:%.o=%.c) + +SRCS= $(LOCAL_SRCS) $(CMN_SRCS) +OBJS= $(LOCAL_OBJS) $(CMN_OBJS) + + +MDNSFLAGS= -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN \ + -DLOG_PERROR=0 -DHAVE_SOLARIS -DHAVE_SOLARIS_ZONES \ + -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME \ + -DHAVE_IPV6=1 -Dasm=__asm -DMDNSD_NOROOT \ + -DPID_FILE=\"\" -DMDNSD_USER=\"noaccess\" + +include ../../../Makefile.cmd + +ROOTMANIFESTDIR= $(ROOTSVCNETWORKDNS) +$(ROOTMANIFEST) := FILEMODE= 444 + +.PARALLEL: $(LOCAL_OBJS) +.WAIT: $(PROG) +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +include ../Makefile.lib + +C99MODE = $(C99_ENABLE) +CPPFLAGS += -D_REENTRANT $(MDNSFLAGS) -I$(CMN_DIR) +LDLIBS += -lsocket -lnsl + +install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST) + +%.o: $(CMN_DIR)/%.c + $(COMPILE.c) $(OUTPUT_OPTION) $< + $(POST_PROCESS_O) + +check: $(CHKMANIFEST) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +include ../../../Makefile.targ diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c new file mode 100644 index 0000000000..00a428f934 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c @@ -0,0 +1,138 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: PlatformCommon.c,v $ +Revision 1.7 2006/08/14 23:24:56 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.6 2005/04/08 21:30:16 ksekar +<rdar://problem/4007457> Compiling problems with mDNSResponder-98 on Solaris/Sparc v9 +Patch submitted by Bernd Kuhls + +Revision 1.5 2005/02/01 19:33:30 ksekar +<rdar://problem/3985239> Keychain format too restrictive + +Revision 1.4 2005/01/19 19:19:21 ksekar +<rdar://problem/3960191> Need a way to turn off domain discovery + +Revision 1.3 2004/12/13 17:46:52 cheshire +Use sizeof(buf) instead of fixed constant 1024 + +Revision 1.2 2004/12/01 03:30:29 cheshire +<rdar://problem/3889346> Add Unicast DNS support to mDNSPosix + +Revision 1.1 2004/12/01 01:51:35 cheshire +Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> // Needed for fopen() etc. +#include <unistd.h> // Needed for close() +#include <string.h> // Needed for strlen() etc. +#include <errno.h> // Needed for errno etc. +#include <sys/socket.h> // Needed for socket() etc. +#include <netinet/in.h> // Needed for sockaddr_in + +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "PlatformCommon.h" + +#ifdef NOT_HAVE_SOCKLEN_T + typedef unsigned int socklen_t; +#endif + +// Bind a UDP socket to a global destination to find the default route's interface address +mDNSexport void FindDefaultRouteIP(mDNSAddr *a) + { + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + int sock = socket(AF_INET,SOCK_DGRAM,0); + a->type = mDNSAddrType_None; + if (sock == -1) return; + addr.sin_family = AF_INET; + addr.sin_port = 1; // Not important, any port and public address will do + addr.sin_addr.s_addr = 0x11111111; + if ((connect(sock,(const struct sockaddr*)&addr,sizeof(addr))) == -1) { close(sock); return; } + if ((getsockname(sock,(struct sockaddr*)&addr, &len)) == -1) { close(sock); return; } + close(sock); + a->type = mDNSAddrType_IPv4; + a->ip.v4.NotAnInteger = addr.sin_addr.s_addr; + } + +// dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length +mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f) + { + char buf[32+1+MAX_ESCAPED_DOMAIN_NAME]; // Option name, one space, option value + unsigned int len = strlen(option); + if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; } + fseek(f, 0, SEEK_SET); // set position to beginning of stream + while (fgets(buf, sizeof(buf), f)) // Read at most sizeof(buf)-1 bytes from file, and append '\0' C-string terminator + { + if (!strncmp(buf, option, len)) + { + strncpy(dst, buf + len + 1, MAX_ESCAPED_DOMAIN_NAME-1); + if (dst[MAX_ESCAPED_DOMAIN_NAME-1]) dst[MAX_ESCAPED_DOMAIN_NAME-1] = '\0'; + len = strlen(dst); + if (len && dst[len-1] == '\n') dst[len-1] = '\0'; // chop newline + return mDNStrue; + } + } + debugf("Option %s not set", option); + return mDNSfalse; + } + +mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled) + { + char buf [MAX_ESCAPED_DOMAIN_NAME]; + char secret[MAX_ESCAPED_DOMAIN_NAME] = ""; + mStatus err; + FILE *f = fopen(filename, "r"); + + if (hostname) hostname->c[0] = 0; + if (domain) domain->c[0] = 0; + if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse; + + if (f) + { + if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue; + if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf; + if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf; + GetConfigOption(secret, "secret-64", f); // failure means no authentication + fclose(f); + f = NULL; + } + else + { + if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened."); + return; + } + + if (domain && domain->c[0] && secret[0]) + { + // for now we assume keyname = service reg domain and we use same key for service and hostname registration + err = mDNS_SetSecretForZone(m, domain, domain, secret); + if (err) LogMsg("ERROR: mDNS_SetSecretForZone returned %d for domain %##s", err, domain->c); + } + + return; + + badf: + LogMsg("ERROR: malformatted config file"); + if (f) fclose(f); + } diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h new file mode 100644 index 0000000000..98f0e85e09 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: PlatformCommon.h,v $ +Revision 1.4 2006/08/14 23:24:56 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.3 2005/01/19 19:19:21 ksekar +<rdar://problem/3960191> Need a way to turn off domain discovery + +Revision 1.2 2004/12/01 03:30:29 cheshire +<rdar://problem/3889346> Add Unicast DNS support to mDNSPosix + +Revision 1.1 2004/12/01 01:51:35 cheshire +Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +extern void FindDefaultRouteIP(mDNSAddr *a); +extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled); diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c new file mode 100644 index 0000000000..b6a92caa5b --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c @@ -0,0 +1,369 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + File: daemon.c + + Contains: main & associated Application layer for mDNSResponder on Linux. + + Change History (most recent first): + +$Log: PosixDaemon.c,v $ +Revision 1.29.2.1 2006/08/29 06:24:34 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.29 2005/08/04 03:37:45 mkrochma +Temporary workaround to fix posix after mDNS_SetPrimaryInterfaceInfo changed + +Revision 1.28 2005/07/19 11:21:09 cheshire +<rdar://problem/4170449> Unix Domain Socket leak in mdnsd + +Revision 1.27 2005/02/04 00:39:59 cheshire +Move ParseDNSServers() from PosixDaemon.c to mDNSPosix.c so all Posix client layers can use it + +Revision 1.26 2005/02/02 02:21:30 cheshire +Update references to "mDNSResponder" to say "mdnsd" instead + +Revision 1.25 2005/01/27 20:01:50 cheshire +udsSupportRemoveFDFromEventLoop() needs to close the file descriptor as well + +Revision 1.24 2005/01/19 19:20:49 ksekar +<rdar://problem/3960191> Need a way to turn off domain discovery + +Revision 1.23 2004/12/16 20:17:11 cheshire +<rdar://problem/3324626> Cache memory management improvements + +Revision 1.22 2004/12/10 13:12:08 cheshire +Create no-op function RecordUpdatedNiceLabel(), required by uds_daemon.c + +Revision 1.21 2004/12/01 20:57:20 ksekar +<rdar://problem/3873921> Wide Area Service Discovery must be split-DNS aware + +Revision 1.20 2004/12/01 04:28:43 cheshire +<rdar://problem/3872803> Darwin patches for Solaris and Suse +Use version of daemon() provided in mDNSUNP.c instead of local copy + +Revision 1.19 2004/12/01 03:30:29 cheshire +<rdar://problem/3889346> Add Unicast DNS support to mDNSPosix + +Revision 1.18 2004/11/30 22:45:59 cheshire +Minor code tidying + +Revision 1.17 2004/11/30 22:18:59 cheshire +<rdar://problem/3889351> Posix needs to read the list of unicast DNS servers and set server list + +Revision 1.16 2004/09/21 21:05:12 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.15 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.14 2004/09/16 00:24:49 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.13 2004/08/11 01:59:41 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init() + +Revision 1.12 2004/06/28 23:19:19 cheshire +Fix "Daemon_Init declared but never defined" warning on Linux + +Revision 1.11 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.10 2004/06/08 04:59:40 cheshire +Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it + +Revision 1.9 2004/05/29 00:14:20 rpantos +<rdar://problem/3508093> Runtime check to disable prod mdnsd on OS X. + +Revision 1.8 2004/04/07 01:19:04 cheshire +Hash slot value should be unsigned + +Revision 1.7 2004/02/14 06:34:57 cheshire +Use LogMsg instead of fprintf( stderr + +Revision 1.6 2004/02/14 01:10:42 rpantos +Allow daemon to run if 'nobody' is not defined, with a warning. (For Roku HD1000.) + +Revision 1.5 2004/02/05 07:45:43 cheshire +Add Log header + +Revision 1.4 2004/01/28 21:14:23 cheshire +Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) + +Revision 1.3 2004/01/19 19:51:46 cheshire +Fix compiler error (mixed declarations and code) on some versions of Linux + +Revision 1.2 2003/12/11 03:03:51 rpantos +Clean up mDNSPosix so that it builds on OS X again. + +Revision 1.1 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <sys/types.h> + +#include "mDNSEmbeddedAPI.h" +#include "mDNSDebug.h" +#include "mDNSPosix.h" +#include "uds_daemon.h" +#include "PlatformCommon.h" +#include "mDNSUNP.h" + +#ifndef MDNSD_USER +#define MDNSD_USER "nobody" +#endif + +#define CONFIG_FILE "/etc/mdnsd.conf" +static domainname DynDNSZone; // Default wide-area zone for service registration +static domainname DynDNSHostname; + +#define RR_CACHE_SIZE 500 +static CacheEntity gRRCache[RR_CACHE_SIZE]; + +extern const char mDNSResponderVersionString[]; + +static void Reconfigure(mDNS *m) + { + mDNSAddr DynDNSIP; + mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL); + mDNS_DeleteDNSServers(m); + if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) + LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); + ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL); + FindDefaultRouteIP(&DynDNSIP); + if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); + if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL); + } + +// Do appropriate things at startup with command line arguments. Calls exit() if unhappy. +static void ParseCmdLinArgs(int argc, char **argv) + { + if (argc > 1) + { + if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue; + else printf("Usage: %s [-debug]\n", argv[0]); + } + + if (!mDNS_DebugMode) + { + int result = daemon(0, 0); + if (result != 0) { LogMsg("Could not run as daemon - exiting"); exit(result); } +#if __APPLE__ + LogMsg("The POSIX mdnsd should only be used on OS X for testing - exiting"); + exit(-1); +#endif + } + } + +static void DumpStateLog(mDNS *const m) +// Dump a little log of what we've been up to. + { + LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----"); + udsserver_info(m); + LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); + } + +static mStatus MainLoop(mDNS *m) // Loop until we quit. + { + sigset_t signals; + mDNSBool gotData = mDNSfalse; + + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + mDNSPosixListenForSignalInEventLoop(SIGUSR1); + mDNSPosixListenForSignalInEventLoop(SIGPIPE); + mDNSPosixListenForSignalInEventLoop(SIGHUP) ; + + for (; ;) + { + // Work out how long we expect to sleep before the next scheduled task + struct timeval timeout; + mDNSs32 ticks; + + // Only idle if we didn't find any data the last time around + if (!gotData) + { + mDNSs32 nextTimerEvent = mDNS_Execute(m); + nextTimerEvent = udsserver_idle(nextTimerEvent); + ticks = nextTimerEvent - mDNS_TimeNow(m); + if (ticks < 1) ticks = 1; + } + else // otherwise call EventLoop again with 0 timemout + ticks = 0; + + timeout.tv_sec = ticks / mDNSPlatformOneSecond; + timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * 1000000 / mDNSPlatformOneSecond; + + (void) mDNSPosixRunEventLoopOnce(m, &timeout, &signals, &gotData); + + if (sigismember(&signals, SIGHUP )) Reconfigure(m); + if (sigismember(&signals, SIGUSR1)) DumpStateLog(m); + // SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up. + if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring"); + if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break; + } + return EINTR; + } + +int main(int argc, char **argv) + { + #define mDNSRecord mDNSStorage + mDNS_PlatformSupport platformStorage; + mStatus err; + + bzero(&mDNSRecord, sizeof mDNSRecord); + bzero(&platformStorage, sizeof platformStorage); + + ParseCmdLinArgs(argc, argv); + + LogMsgIdent(mDNSResponderVersionString, "starting"); + + err = mDNS_Init(&mDNSRecord, &platformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + + if (mStatus_NoError == err) + err = udsserver_init(); + + Reconfigure(&mDNSRecord); + + // Now that we're finished with anything privileged, switch over to running as "nobody" + if (mStatus_NoError == err) + { + const struct passwd *pw = getpwnam(MDNSD_USER); + if (pw != NULL) + setuid(pw->pw_uid); + else +#ifdef MDNSD_NOROOT + { + LogMsg("WARNING: mdnsd exiting because user \""MDNSD_USER"\" does not exist"); + err = mStatus_Invalid; + } +#else + LogMsg("WARNING: mdnsd continuing as root because user \""MDNSD_USER"\" does not exist"); +#endif + } + + if (mStatus_NoError == err) + err = MainLoop(&mDNSRecord); + + LogMsgIdent(mDNSResponderVersionString, "stopping"); + + mDNS_Close(&mDNSRecord); + + if (udsserver_exit() < 0) + LogMsg("ExitCallback: udsserver_exit failed"); + + #if MDNS_DEBUGMSGS > 0 + printf("mDNSResponder exiting normally with %ld\n", err); + #endif + + return err; + } + +// uds_daemon support //////////////////////////////////////////////////////////// + +#undef LogMalloc + +#if MDNS_MALLOC_DEBUGGING >= 2 +#define LogMalloc LogMsg +#else +#define LogMalloc(ARGS...) ((void)0) +#endif + +mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context) +/* Support routine for uds_daemon.c */ + { + // Depends on the fact that udsEventCallback == mDNSPosixEventCallback + return mDNSPosixAddFDToEventLoop(fd, callback, context); + } + +mStatus udsSupportRemoveFDFromEventLoop(int fd) // Note: This also CLOSES the file descriptor + { + mStatus err = mDNSPosixRemoveFDFromEventLoop(fd); + close(fd); + return err; + } + +mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) + { + (void)m; + (void)delay; + // No-op, for now + } + +#if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + +void *mallocL(char *msg, unsigned int size) + { + unsigned long *mem = malloc(size+8); + if (!mem) + { + LogMsg("malloc( %s : %d ) failed", msg, size); + return(NULL); + } + else + { + LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]); + mem[0] = 0xDEAD1234; + mem[1] = size; + //bzero(&mem[2], size); + memset(&mem[2], 0xFF, size); +// validatelists(&mDNSStorage); + return(&mem[2]); + } + } + +void freeL(char *msg, void *x) + { + if (!x) + LogMsg("free( %s @ NULL )!", msg); + else + { + unsigned long *mem = ((unsigned long *)x) - 2; + if (mem[0] != 0xDEAD1234) + { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; } + if (mem[1] > 8000) + { LogMsg("free( %s : %ld @ %p) too big!", msg, mem[1], &mem[2]); return; } + LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]); + //bzero(mem, mem[1]+8); + memset(mem, 0xDD, mem[1]+8); +// validatelists(&mDNSStorage); + free(mem); + } + } + +#endif // MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + +// For convenience when using the "strings" command, this is the last thing in the file +#if mDNSResponderVersion > 1 +mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ") "; +#else +mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ") "; +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c new file mode 100644 index 0000000000..2708aa0cac --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c @@ -0,0 +1,7362 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This code is completely 100% portable C. It does not depend on any external header files + * from outside the mDNS project -- all the types it expects to find are defined right here. + * + * The previous point is very important: This file does not depend on any external + * header files. It should complile on *any* platform that has a C compiler, without + * making *any* assumptions about availability of so-called "standard" C functions, + * routines, or types (which may or may not be present on any given platform). + + * Formatting notes: + * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion + * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>, + * but for the sake of brevity here I will say just this: Curly braces are not syntactially + * part of an "if" statement; they are the beginning and ending markers of a compound statement; + * therefore common sense dictates that if they are part of a compound statement then they + * should be indented to the same level as everything else in that compound statement. + * Indenting curly braces at the same level as the "if" implies that curly braces are + * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" + * thinking that variables x and y are both of type "char*" -- and anyone who doesn't + * understand why variable y is not of type "char*" just proves the point that poor code + * layout leads people to unfortunate misunderstandings about how the C language really works.) + + Change History (most recent first): + +$Log: mDNS.c,v $ +Revision 1.537.2.1 2006/08/29 06:24:22 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.537 2006/03/19 02:00:07 cheshire +<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions + +Revision 1.536 2006/03/08 23:29:53 cheshire +<rdar://problem/4468716> Improve "Service Renamed" log message + +Revision 1.535 2006/03/02 20:41:17 cheshire +<rdar://problem/4111464> After record update, old record sometimes remains in cache +Minor code tidying and comments to reduce the risk of similar programming errors in future + +Revision 1.534 2006/03/02 03:25:46 cheshire +<rdar://problem/4111464> After record update, old record sometimes remains in cache +Code to harmonize RRSet TTLs was inadvertently rescuing expiring records + +Revision 1.533 2006/02/26 00:54:41 cheshire +Fixes to avoid code generation warning/error on FreeBSD 7 + +Revision 1.532 2005/12/02 20:24:36 cheshire +<rdar://problem/4363209> Adjust cutoff time for KA list by one second + +Revision 1.531 2005/12/02 19:05:42 cheshire +Tidy up constants + +Revision 1.530 2005/11/07 01:49:48 cheshire +For consistency, use NonZeroTime() function instead of ?: expression + +Revision 1.529 2005/10/25 23:42:24 cheshire +<rdar://problem/4316057> Error in ResolveSimultaneousProbe() when type or class don't match +Changed switch statement to an "if" + +Revision 1.528 2005/10/25 23:34:22 cheshire +<rdar://problem/4316048> RequireGoodbye state not set/respected sometimes when machine going to sleep + +Revision 1.527 2005/10/25 22:43:59 cheshire +Add clarifying comments + +Revision 1.526 2005/10/20 00:10:33 cheshire +<rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code + +Revision 1.525 2005/09/24 00:47:17 cheshire +Fix comment typos + +Revision 1.524 2005/09/16 21:06:49 cheshire +Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place + +Revision 1.523 2005/03/21 00:33:51 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.522 2005/03/04 21:48:12 cheshire +<rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity + +Revision 1.521 2005/02/25 04:21:00 cheshire +<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing + +Revision 1.520 2005/02/16 01:14:11 cheshire +Convert RR Cache LogOperation() calls to debugf() + +Revision 1.519 2005/02/15 01:57:20 cheshire +When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero + +Revision 1.518 2005/02/10 22:35:17 cheshire +<rdar://problem/3727944> Update name + +Revision 1.517 2005/02/03 00:21:21 cheshire +Update comments about BIND named and zero-length TXT records + +Revision 1.516 2005/01/28 06:06:32 cheshire +Update comment + +Revision 1.515 2005/01/27 00:21:49 cheshire +<rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message + +Revision 1.514 2005/01/21 01:33:45 cheshire +<rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM + +Revision 1.513 2005/01/21 00:07:54 cheshire +<rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict + +Revision 1.512 2005/01/20 00:37:45 cheshire +<rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse +Take care not to recycle records while they are on the CacheFlushRecords list + +Revision 1.511 2005/01/19 22:48:53 cheshire +<rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService() + +Revision 1.510 2005/01/19 03:12:45 cheshire +Move LocalRecordReady() macro from mDNS.c to DNSCommon.h + +Revision 1.509 2005/01/19 03:08:49 cheshire +<rdar://problem/3961051> CPU Spin in mDNSResponder +Log messages to help catch and report CPU spins + +Revision 1.508 2005/01/18 18:56:32 cheshire +<rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate + +Revision 1.507 2005/01/18 01:12:07 cheshire +<rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes + +Revision 1.506 2005/01/17 23:28:53 cheshire +Fix compile error + +Revision 1.505 2005/01/11 02:02:56 shersche +Move variable declaration to the beginning of statement block + +Revision 1.504 2004/12/20 20:24:35 cheshire +<rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer + +Revision 1.503 2004/12/20 18:41:47 cheshire +<rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space + +Revision 1.502 2004/12/20 18:04:08 cheshire +<rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache + +Revision 1.501 2004/12/19 23:50:18 cheshire +<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records +Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services + +Revision 1.500 2004/12/18 03:13:46 cheshire +<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records + +Revision 1.499 2004/12/17 23:37:45 cheshire +<rdar://problem/3485365> Guard against repeating wireless dissociation/re-association +(and other repetitive configuration changes) + +Revision 1.498 2004/12/17 05:25:46 cheshire +<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs + +Revision 1.497 2004/12/17 03:20:58 cheshire +<rdar://problem/3925168> Don't send unicast replies we know will be ignored + +Revision 1.496 2004/12/16 22:18:26 cheshire +Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces + +Revision 1.495 2004/12/16 21:27:37 ksekar +Fixed build failures when compiled with verbose debugging messages + +Revision 1.494 2004/12/16 20:46:56 cheshire +Fix compiler warnings + +Revision 1.493 2004/12/16 20:13:00 cheshire +<rdar://problem/3324626> Cache memory management improvements + +Revision 1.492 2004/12/16 08:03:24 shersche +Fix compilation error when UNICAST_DISABLED is set + +Revision 1.491 2004/12/11 01:52:11 cheshire +<rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too + +Revision 1.490 2004/12/10 20:06:25 cheshire +<rdar://problem/3915074> Reduce egregious stack space usage +Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes + +Revision 1.489 2004/12/10 20:03:43 cheshire +<rdar://problem/3915074> Reduce egregious stack space usage +Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes + +Revision 1.488 2004/12/10 19:50:41 cheshire +<rdar://problem/3915074> Reduce egregious stack space usage +Reduced SendResponses() stack frame from 9K to 176 bytes + +Revision 1.487 2004/12/10 19:39:13 cheshire +<rdar://problem/3915074> Reduce egregious stack space usage +Reduced SendQueries() stack frame from 18K to 112 bytes + +Revision 1.486 2004/12/10 14:16:17 cheshire +<rdar://problem/3889788> Relax update rate limiting +We now allow an average rate of ten updates per minute. +Updates in excess of that are rate limited, but more gently than before. + +Revision 1.485 2004/12/10 02:09:24 cheshire +<rdar://problem/3898376> Modify default TTLs + +Revision 1.484 2004/12/09 03:15:40 ksekar +<rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains + +Revision 1.483 2004/12/07 23:00:14 ksekar +<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration: +Call RecordProbeFailure even if there is no record callback + +Revision 1.482 2004/12/07 22:49:06 cheshire +<rdar://problem/3908850> BIND doesn't allow zero-length TXT records + +Revision 1.481 2004/12/07 21:26:04 ksekar +<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration + +Revision 1.480 2004/12/07 20:42:33 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.479 2004/12/07 17:50:49 ksekar +<rdar://problem/3908850> BIND doesn't allow zero-length TXT records + +Revision 1.478 2004/12/06 21:15:22 ksekar +<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.477 2004/12/04 02:12:45 cheshire +<rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.476 2004/11/29 23:34:31 cheshire +On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero +is crude, and effectively halves the time resolution. The more selective NonZeroTime() function +only nudges the time value to 1 if the interval calculation happens to result in the value zero. + +Revision 1.475 2004/11/29 23:13:31 cheshire +<rdar://problem/3484552> All unique records in a set should have the cache flush bit set +Additional check: Make sure we don't unnecessarily send packets containing only additionals. +(This could occur with multi-packet KA lists, if the answer and additionals were marked +by the query packet, and then the answer were later suppressed in a subsequent KA packet.) + +Revision 1.474 2004/11/29 17:18:12 cheshire +Remove "Unknown DNS packet type" message for update responses + +Revision 1.473 2004/11/25 01:57:52 cheshire +<rdar://problem/3484552> All unique records in a set should have the cache flush bit set + +Revision 1.472 2004/11/25 01:28:09 cheshire +<rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too) + +Revision 1.471 2004/11/25 01:10:13 cheshire +Move code to add additional records to a subroutine called AddAdditionalsToResponseList() + +Revision 1.470 2004/11/24 21:54:44 cheshire +<rdar://problem/3894475> mDNSCore not receiving unicast responses properly + +Revision 1.469 2004/11/24 04:50:39 cheshire +Minor tidying + +Revision 1.468 2004/11/24 01:47:07 cheshire +<rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success. + +Revision 1.467 2004/11/24 01:41:28 cheshire +Rename CompleteProbing() to AcknowledgeRecord() + +Revision 1.466 2004/11/23 21:08:07 ksekar +Don't use ID to demux multicast/unicast now that unicast uses random IDs + +Revision 1.465 2004/11/15 20:09:21 ksekar +<rdar://problem/3719050> Wide Area support for Add/Remove record + +Revision 1.464 2004/11/03 01:44:36 cheshire +Update debugging messages + +Revision 1.463 2004/10/29 02:38:48 cheshire +Fix Windows compile errors + +Revision 1.462 2004/10/28 19:21:07 cheshire +Guard against registering interface with zero InterfaceID + +Revision 1.461 2004/10/28 19:02:16 cheshire +Remove \n from LogMsg() call + +Revision 1.460 2004/10/28 03:24:40 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.459 2004/10/26 22:34:37 cheshire +<rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding + +Revision 1.458 2004/10/26 20:45:28 cheshire +Show mask in "invalid mask" message + +Revision 1.457 2004/10/26 06:28:36 cheshire +Now that we don't check IP TTL any more, remove associated log message + +Revision 1.456 2004/10/26 06:21:42 cheshire +Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address) + +Revision 1.455 2004/10/26 06:11:40 cheshire +Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed + +Revision 1.454 2004/10/23 01:16:00 cheshire +<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts + +Revision 1.453 2004/10/22 20:52:06 ksekar +<rdar://problem/3799260> Create NAT port mappings for Long Lived Queries + +Revision 1.452 2004/10/20 01:50:40 cheshire +<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API +Implemented ForceMCast mode for AuthRecords as well as for Questions + +Revision 1.451 2004/10/19 21:33:15 cheshire +<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.450 2004/10/19 17:42:59 ksekar +Fixed compiler warnings for non-debug builds. + +Revision 1.449 2004/10/18 22:57:07 cheshire +<rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1 + +Revision 1.448 2004/10/16 00:16:59 cheshire +<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check + +Revision 1.447 2004/10/15 00:51:21 cheshire +<rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1 + +Revision 1.446 2004/10/14 00:43:34 cheshire +<rdar://problem/3815984> Services continue to announce SRV and HINFO + +Revision 1.445 2004/10/12 21:07:09 cheshire +Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit() + +Revision 1.444 2004/10/11 17:54:16 ksekar +Changed hashtable pointer output from debugf to verbosedebugf. + +Revision 1.443 2004/10/10 07:05:45 cheshire +For consistency, use symbol "localdomain" instead of literal string + +Revision 1.442 2004/10/08 20:25:10 cheshire +Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time + +Revision 1.441 2004/10/08 03:25:01 ksekar +<rdar://problem/3831716> domain enumeration should use LLQs + +Revision 1.440 2004/10/06 01:44:19 cheshire +<rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record + +Revision 1.439 2004/10/03 23:14:11 cheshire +Add "mDNSEthAddr" type and "zeroEthAddr" constant + +Revision 1.438 2004/09/29 23:07:04 cheshire +Patch from Pavel Repin to fix compile error on Windows + +Revision 1.437 2004/09/28 02:23:50 cheshire +<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events +Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot +For records with the cache flush bit set, defer the decision until the end of the packet + +Revision 1.436 2004/09/28 01:27:04 cheshire +Update incorrect log message + +Revision 1.435 2004/09/25 02:41:39 cheshire +<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events + +Revision 1.434 2004/09/25 02:32:06 cheshire +Update comments + +Revision 1.433 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.432 2004/09/24 21:35:17 cheshire +<rdar://problem/3561220> Browses are no longer piggybacking on other browses +TargetPort and TargetQID are allowed to be undefined if no question->Target is set + +Revision 1.431 2004/09/24 21:33:12 cheshire +Adjust comment + +Revision 1.430 2004/09/24 02:15:49 cheshire +<rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces + +Revision 1.429 2004/09/24 00:20:21 cheshire +<rdar://problem/3483349> Any rrtype is a conflict for unique records + +Revision 1.428 2004/09/24 00:12:25 cheshire +Get rid of unused RRUniqueOrKnownUnique(RR) + +Revision 1.427 2004/09/23 20:44:11 cheshire +<rdar://problem/3813148> Reduce timeout before expiring records on failure + +Revision 1.426 2004/09/23 20:21:07 cheshire +<rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic +Associate a unique sequence number with each received packet, and only increment the count of recent answer +packets if the packet sequence number for this answer record is not one we've already seen and counted. + +Revision 1.425 2004/09/23 20:14:38 cheshire +Rename "question->RecentAnswers" to "question->RecentAnswerPkts" + +Revision 1.424 2004/09/23 00:58:36 cheshire +<rdar://problem/3781269> Rate limiting interferes with updating TXT records + +Revision 1.423 2004/09/23 00:50:53 cheshire +<rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep + +Revision 1.422 2004/09/22 02:34:46 cheshire +Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h + +Revision 1.421 2004/09/21 23:29:49 cheshire +<rdar://problem/3680045> DNSServiceResolve should delay sending packets + +Revision 1.420 2004/09/21 23:01:42 cheshire +Update debugf messages + +Revision 1.419 2004/09/21 19:51:14 cheshire +Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c + +Revision 1.418 2004/09/21 18:40:17 cheshire +<rdar://problem/3376752> Adjust default record TTLs + +Revision 1.417 2004/09/21 17:32:16 cheshire +<rdar://problem/3809484> Rate limiting imposed too soon + +Revision 1.416 2004/09/20 23:52:01 cheshire +CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c + +Revision 1.415 2004/09/18 01:14:09 cheshire +<rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces + +Revision 1.414 2004/09/18 01:06:48 cheshire +Add comments + +Revision 1.413 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.412 2004/09/17 00:46:33 cheshire +mDNS_TimeNow should take const mDNS parameter + +Revision 1.411 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.410 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.409 2004/09/16 21:59:15 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.408 2004/09/16 21:36:36 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.407 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.406 2004/09/16 01:58:14 cheshire +Fix compiler warnings + +Revision 1.405 2004/09/16 00:24:48 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.404 2004/09/15 21:44:11 cheshire +<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init +Show time value in log to help diagnose errors + +Revision 1.403 2004/09/15 00:46:32 ksekar +Changed debugf to verbosedebugf in CheckCacheExpiration + +Revision 1.402 2004/09/14 23:59:55 cheshire +<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init + +Revision 1.401 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.400 2004/09/02 03:48:47 cheshire +<rdar://problem/3709039> Disable targeted unicast query support by default +1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record +2. New field AllowRemoteQuery in AuthRecord structure +3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set +4. mDNS.c only answers remote queries if AllowRemoteQuery is set + +Revision 1.399 2004/09/02 01:39:40 cheshire +For better readability, follow consistent convention that QR bit comes first, followed by OP bits + +Revision 1.398 2004/09/01 03:59:29 ksekar +<rdar://problem/3783453>: Conditionally compile out uDNS code on Windows + +Revision 1.397 2004/08/25 22:04:25 rpantos +Fix the standard Windows compile error. + +Revision 1.396 2004/08/25 00:37:27 ksekar +<rdar://problem/3774635>: Cleanup DynDNS hostname registration code + +Revision 1.395 2004/08/18 17:21:18 ksekar +Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs() + +Revision 1.394 2004/08/14 03:22:41 cheshire +<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.393 2004/08/13 23:42:52 cheshire +Removed unused "zeroDomainNamePtr" + +Revision 1.392 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.391 2004/08/13 23:25:00 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + +Revision 1.390 2004/08/11 02:17:01 cheshire +<rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record + +Revision 1.389 2004/08/10 23:19:14 ksekar +<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.388 2004/07/30 17:40:06 ksekar +<rdar://problem/3739115>: TXT Record updates not available for wide-area services + +Revision 1.387 2004/07/26 22:49:30 ksekar +<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client + +Revision 1.386 2004/07/13 21:24:24 rpantos +Fix for <rdar://problem/3701120>. + +Revision 1.385 2004/06/18 19:09:59 cheshire +<rdar://problem/3588761> Current method of doing subtypes causes name collisions + +Revision 1.384 2004/06/15 04:31:23 cheshire +Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion() + +Revision 1.383 2004/06/11 00:04:59 cheshire +<rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord + +Revision 1.382 2004/06/08 04:59:40 cheshire +Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it + +Revision 1.381 2004/06/05 00:57:30 cheshire +Remove incorrect LogMsg() + +Revision 1.380 2004/06/05 00:04:26 cheshire +<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration + +Revision 1.379 2004/05/28 23:42:36 ksekar +<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.378 2004/05/25 17:25:25 cheshire +Remove extraneous blank lines and white space + +Revision 1.377 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.376 2004/05/05 18:30:44 ksekar +Restored surpressed Cache Tail debug messages. + +Revision 1.375 2004/04/26 21:36:25 cheshire +Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive +is indicated as being available on that interface + +Revision 1.374 2004/04/21 02:53:26 cheshire +Typo in debugf statement + +Revision 1.373 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.372 2004/04/21 02:38:51 cheshire +Add debugging checks + +Revision 1.371 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.370 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field + +Revision 1.369 2004/04/09 16:34:00 cheshire +Debugging code for later; currently unused + +Revision 1.368 2004/04/02 19:19:48 cheshire +Add code to do optional logging of multi-packet KA list time intervals + +Revision 1.367 2004/03/20 03:16:10 cheshire +Minor refinement to "Excessive update rate" message + +Revision 1.366 2004/03/20 03:12:57 cheshire +<rdar://problem/3587619>: UpdateCredits not granted promptly enough + +Revision 1.365 2004/03/19 23:51:22 cheshire +Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60) + +Revision 1.364 2004/03/13 01:57:33 ksekar +<rdar://problem/3192546>: DynDNS: Dynamic update of service records + +Revision 1.363 2004/03/12 21:00:51 cheshire +Also show port numbers when logging "apparent spoof mDNS Response" messages + +Revision 1.362 2004/03/12 08:58:18 cheshire +Guard against empty TXT records + +Revision 1.361 2004/03/09 03:00:46 cheshire +<rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good. + +Revision 1.360 2004/03/08 02:52:41 cheshire +Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages + +Revision 1.359 2004/03/02 03:21:56 cheshire +<rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries + +Revision 1.358 2004/02/20 08:18:34 cheshire +<rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily + +Revision 1.357 2004/02/18 01:47:41 cheshire +<rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms + +Revision 1.356 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.355 2004/02/05 09:32:33 cheshire +Fix from Bob Bradley: When using the "%.*s" string form, +guard against truncating in the middle of a multi-byte UTF-8 character. + +Revision 1.354 2004/02/05 09:30:22 cheshire +Update comments + +Revision 1.353 2004/01/28 03:41:00 cheshire +<rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries + +Revision 1.352 2004/01/28 02:30:07 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.351 2004/01/27 20:15:22 cheshire +<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 + +Revision 1.350 2004/01/24 23:38:16 cheshire +Use mDNSVal16() instead of shifting and ORing operations + +Revision 1.349 2004/01/23 23:23:14 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.348 2004/01/22 03:54:11 cheshire +Create special meta-interface 'mDNSInterface_ForceMCast' (-2), +which means "do this query via multicast, even if it's apparently a unicast domain" + +Revision 1.347 2004/01/22 03:50:49 cheshire +If the client has specified an explicit InterfaceID, then do query by multicast, not unicast + +Revision 1.346 2004/01/22 03:48:41 cheshire +Make sure uDNS client doesn't accidentally use query ID zero + +Revision 1.345 2004/01/22 03:43:08 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.344 2004/01/21 21:53:18 cheshire +<rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.343 2003/12/23 00:07:47 cheshire +Make port number in debug message be five-character field, left justified + +Revision 1.342 2003/12/20 01:34:28 cheshire +<rdar://problem/3515876>: Error putting additional records into packets +Another fix from Rampi: responseptr needs to be updated inside the "for" loop, +after every record, not once at the end. + +Revision 1.341 2003/12/18 22:56:12 cheshire +<rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets + +Revision 1.340 2003/12/16 02:31:37 cheshire +Minor update to comments + +Revision 1.339 2003/12/13 05:50:33 bradley +Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform +layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to +fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf. + +Revision 1.338 2003/12/13 03:05:27 ksekar +<rdar://problem/3192548>: DynDNS: Unicast query of service records + +Revision 1.337 2003/12/01 21:46:05 cheshire +mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist + +Revision 1.336 2003/12/01 21:26:19 cheshire +Guard against zero-length sbuffer in mDNS_vsnprintf() + +Revision 1.335 2003/12/01 20:27:48 cheshire +Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors + +Revision 1.334 2003/11/20 22:59:53 cheshire +Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h +Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. + +Revision 1.333 2003/11/20 20:49:53 cheshire +Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures + +Revision 1.332 2003/11/20 05:47:37 cheshire +<rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query +Now that we only include answers in the known answer list if they are less than +halfway to expiry, the check to also see if we have another query scheduled +before the record expires is no longer necessary (and in fact, not correct). + +Revision 1.331 2003/11/19 22:31:48 cheshire +When automatically adding A records to SRVs, add them as additionals, not answers + +Revision 1.330 2003/11/19 22:28:50 cheshire +Increment/Decrement mDNS_reentrancy around calls to m->MainCallback() +to allow client to make mDNS calls (specifically the call to mDNS_GrowCache()) + +Revision 1.329 2003/11/19 22:19:24 cheshire +Show log message when ignoring packets with bad TTL. +This is to help diagnose problems on Linux versions that may not report the TTL reliably. + +Revision 1.328 2003/11/19 22:06:38 cheshire +Show log messages when a service or hostname is renamed + +Revision 1.327 2003/11/19 22:03:44 cheshire +Move common "m->NextScheduledResponse = m->timenow" to before "if" statement + +Revision 1.326 2003/11/17 22:27:02 cheshire +Another fix from ramaprasad.kr@hp.com: Improve reply delay computation +on platforms that have native clock rates below fifty ticks per second. + +Revision 1.325 2003/11/17 20:41:44 cheshire +Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls. + +Revision 1.324 2003/11/17 20:36:32 cheshire +Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and +DeadvertiseInterface() -- they're internal private routines, not API routines. + +Revision 1.323 2003/11/14 20:59:08 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.322 2003/11/14 19:47:52 cheshire +Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString + +Revision 1.321 2003/11/14 19:18:34 cheshire +Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too + +Revision 1.320 2003/11/13 06:45:04 cheshire +Fix compiler warning on certain compilers + +Revision 1.319 2003/11/13 00:47:40 cheshire +<rdar://problem/3437556> We should delay AAAA record query if A record already in cache. + +Revision 1.318 2003/11/13 00:33:26 cheshire +Change macro "RRIsAddressType" to "RRTypeIsAddressType" + +Revision 1.317 2003/11/13 00:10:49 cheshire +<rdar://problem/3436412>: Verify that rr data is different before updating. + +Revision 1.316 2003/11/08 23:37:54 cheshire +Give explicit zero initializers to blank static structure, required by certain compilers. +(Thanks to ramaprasad.kr@hp.com for reporting this.) + +Revision 1.315 2003/11/07 03:32:56 cheshire +<rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order +This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes +purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is +to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR() +can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place. + +Revision 1.314 2003/11/07 03:19:49 cheshire +Minor variable renaming for clarity + +Revision 1.313 2003/11/07 03:14:49 cheshire +Previous checkin proved to be overly simplistic; reversing + +Revision 1.312 2003/11/03 23:45:15 cheshire +<rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order +Build cache lists in FIFO order, not customary C LIFO order +(Append new elements to tail of cache list, instead of prepending at the head.) + +Revision 1.311 2003/10/09 18:00:11 cheshire +Another compiler warning fix. + +Revision 1.310 2003/10/07 20:27:05 cheshire +Patch from Bob Bradley, to fix warning and compile error on Windows + +Revision 1.309 2003/09/26 01:06:36 cheshire +<rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too +Made new routine HaveSentEntireRRSet() to check if flag should be set + +Revision 1.308 2003/09/23 01:05:01 cheshire +Minor changes to comments and debugf() message + +Revision 1.307 2003/09/09 20:13:30 cheshire +<rdar://problem/3411105> Don't send a Goodbye record if we never announced it +Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented +rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount + +Revision 1.306 2003/09/09 03:00:03 cheshire +<rdar://problem/3413099> Services take a long time to disappear when switching networks. +Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect + +Revision 1.305 2003/09/09 02:49:31 cheshire +<rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep + +Revision 1.304 2003/09/09 02:41:19 cheshire +<rdar://problem/3411105> Don't send a Goodbye record if we never announced it + +Revision 1.303 2003/09/05 19:55:02 cheshire +<rdar://problem/3409533> Include address records when announcing SRV records + +Revision 1.302 2003/09/05 00:01:36 cheshire +<rdar://problem/3407549> Don't accelerate queries that have large KA lists + +Revision 1.301 2003/09/04 22:51:13 cheshire +<rdar://problem/3398213> Group probes and goodbyes better + +Revision 1.300 2003/09/03 02:40:37 cheshire +<rdar://problem/3404842> mDNSResponder complains about '_'s +Underscores are not supposed to be legal in standard DNS names, but IANA appears +to have allowed them in previous service name registrations, so we should too. + +Revision 1.299 2003/09/03 02:33:09 cheshire +<rdar://problem/3404795> CacheRecordRmv ERROR +Don't update m->NewQuestions until *after* CheckCacheExpiration(); + +Revision 1.298 2003/09/03 01:47:01 cheshire +<rdar://problem/3319418> Services always in a state of flux +Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds + +Revision 1.297 2003/08/29 19:44:15 cheshire +<rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears +1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries + that already have at least one unique answer in the cache +2. For these queries, go straight to QM, skipping QU + +Revision 1.296 2003/08/29 19:08:21 cheshire +<rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep +Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time. + +Revision 1.295 2003/08/28 01:10:59 cheshire +<rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst + +Revision 1.294 2003/08/27 02:30:22 cheshire +<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() +One more change: "query->GotTXT" is now a straightforward bi-state boolean again + +Revision 1.293 2003/08/27 02:25:31 cheshire +<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() + +Revision 1.292 2003/08/21 19:27:36 cheshire +<rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL + +Revision 1.291 2003/08/21 18:57:44 cheshire +<rdar://problem/3387140> Synchronized queries on the network + +Revision 1.290 2003/08/21 02:25:23 cheshire +Minor changes to comments and debugf() messages + +Revision 1.289 2003/08/21 02:21:50 cheshire +<rdar://problem/3386473> Efficiency: Reduce repeated queries + +Revision 1.288 2003/08/20 23:39:30 cheshire +<rdar://problem/3344098> Review syslog messages, and remove as appropriate + +Revision 1.287 2003/08/20 20:47:18 cheshire +Fix compiler warning + +Revision 1.286 2003/08/20 02:18:51 cheshire +<rdar://problem/3344098> Cleanup: Review syslog messages + +Revision 1.285 2003/08/20 01:59:06 cheshire +<rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata +Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place + +Revision 1.284 2003/08/19 22:20:00 cheshire +<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.283 2003/08/19 22:16:27 cheshire +Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case. + +Revision 1.282 2003/08/19 06:48:25 cheshire +<rdar://problem/3376552> Guard against excessive record updates +Each record starts with 10 UpdateCredits. +Every update consumes one UpdateCredit. +UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. +As the number of UpdateCredits declines, the number of announcements is similarly scaled back. +When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. + +Revision 1.281 2003/08/19 04:49:28 cheshire +<rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right +1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6. +2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. +3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. + +Revision 1.280 2003/08/19 02:33:36 cheshire +Update comments + +Revision 1.279 2003/08/19 02:31:11 cheshire +<rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries +Final expiration queries now only mark the question for sending on the particular interface +pertaining to the record that's expiring. + +Revision 1.278 2003/08/18 22:53:37 cheshire +<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime() + +Revision 1.277 2003/08/18 19:05:44 cheshire +<rdar://problem/3382423> UpdateRecord not working right +Added "newrdlength" field to hold new length of updated rdata + +Revision 1.276 2003/08/16 03:39:00 cheshire +<rdar://problem/3338440> InterfaceID -1 indicates "local only" + +Revision 1.275 2003/08/16 02:51:27 cheshire +<rdar://problem/3366590> mDNSResponder takes too much RPRVT +Don't try to compute namehash etc, until *after* validating the name + +Revision 1.274 2003/08/16 01:12:40 cheshire +<rdar://problem/3366590> mDNSResponder takes too much RPRVT +Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a +simple C structure assignment of a domainname, because that object is defined to be 256 bytes long, +and in the process of copying it, the C compiler may run off the end of the rdata object into +unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a +call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid. + +Revision 1.273 2003/08/15 20:16:02 cheshire +<rdar://problem/3366590> mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.272 2003/08/14 19:29:04 cheshire +<rdar://problem/3378473> Include cache records in SIGINFO output +Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them + +Revision 1.271 2003/08/14 02:17:05 cheshire +<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.270 2003/08/13 17:07:28 ksekar +<rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash +Added check to result of mDNS_Register() before linking extra record into list. + +Revision 1.269 2003/08/12 19:56:23 cheshire +Update to APSL 2.0 + +Revision 1.268 2003/08/12 15:01:10 cheshire +Add comments + +Revision 1.267 2003/08/12 14:59:27 cheshire +<rdar://problem/3374490> Rate-limiting blocks some legitimate responses +When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine +whether to suppress the response, also check LastMCInterface to see if it matches. + +Revision 1.266 2003/08/12 12:47:16 cheshire +In mDNSCoreMachineSleep debugf message, display value of m->timenow + +Revision 1.265 2003/08/11 20:04:28 cheshire +<rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache + +Revision 1.264 2003/08/09 00:55:02 cheshire +<rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU +Don't scan the whole cache after every packet. + +Revision 1.263 2003/08/09 00:35:29 cheshire +Moved AnswerNewQuestion() later in the file, in preparation for next checkin + +Revision 1.262 2003/08/08 19:50:33 cheshire +<rdar://problem/3370332> Remove "Cache size now xxx" messages + +Revision 1.261 2003/08/08 19:18:45 cheshire +<rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug + +Revision 1.260 2003/08/08 18:55:48 cheshire +<rdar://problem/3370365> Guard against time going backwards + +Revision 1.259 2003/08/08 18:36:04 cheshire +<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.258 2003/08/08 16:22:05 cheshire +<rdar://problem/3335473> Need to check validity of TXT (and other) records +Remove unneeded LogMsg + +Revision 1.257 2003/08/07 01:41:08 cheshire +<rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones) + +Revision 1.256 2003/08/06 23:25:51 cheshire +<rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four + +Revision 1.255 2003/08/06 23:22:50 cheshire +Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours) + +Revision 1.254 2003/08/06 21:33:39 cheshire +Fix compiler warnings on PocketPC 2003 (Windows CE) + +Revision 1.253 2003/08/06 20:43:57 cheshire +<rdar://problem/3335473> Need to check validity of TXT (and other) records +Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update() + +Revision 1.252 2003/08/06 20:35:47 cheshire +Enhance debugging routine GetRRDisplayString() so it can also be used to display +other RDataBody objects, not just the one currently attached the given ResourceRecord + +Revision 1.251 2003/08/06 19:07:34 cheshire +<rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should +Was checking LastAPTime instead of LastMCTime + +Revision 1.250 2003/08/06 19:01:55 cheshire +Update comments + +Revision 1.249 2003/08/06 00:13:28 cheshire +Tidy up debugf messages + +Revision 1.248 2003/08/05 22:20:15 cheshire +<rdar://problem/3330324> Need to check IP TTL on responses + +Revision 1.247 2003/08/05 00:56:39 cheshire +<rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed + +Revision 1.246 2003/08/04 19:20:49 cheshire +Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages + +Revision 1.245 2003/08/02 01:56:29 cheshire +For debugging: log message if we ever get more than one question in a truncated packet + +Revision 1.244 2003/08/01 23:55:32 cheshire +Fix for compiler warnings on Windows, submitted by Bob Bradley + +Revision 1.243 2003/07/25 02:26:09 cheshire +Typo: FIxed missing semicolon + +Revision 1.242 2003/07/25 01:18:41 cheshire +Fix memory leak on shutdown in mDNS_Close() (detected in Windows version) + +Revision 1.241 2003/07/23 21:03:42 cheshire +Only show "Found record..." debugf message in verbose mode + +Revision 1.240 2003/07/23 21:01:11 cheshire +<rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one +After sending a packet, suppress further sending for the next 100ms. + +Revision 1.239 2003/07/22 01:30:05 cheshire +<rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once + +Revision 1.238 2003/07/22 00:10:20 cheshire +<rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters + +Revision 1.237 2003/07/19 03:23:13 cheshire +<rdar://problem/2986147> mDNSResponder needs to receive and cache larger records + +Revision 1.236 2003/07/19 03:04:55 cheshire +Fix warnings; some debugf message improvements + +Revision 1.235 2003/07/19 00:03:32 cheshire +<rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received +ScheduleNextTask is quite an expensive operation. +We don't need to do all that work after receiving a no-op packet that didn't change our state. + +Revision 1.234 2003/07/18 23:52:11 cheshire +To improve consistency of field naming, global search-and-replace: +NextProbeTime -> NextScheduledProbe +NextResponseTime -> NextScheduledResponse + +Revision 1.233 2003/07/18 00:29:59 cheshire +<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.232 2003/07/18 00:11:38 cheshire +Add extra case to switch statements to handle HINFO data for Get, Put and Display +(In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT) + +Revision 1.231 2003/07/18 00:06:37 cheshire +To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->" + +Revision 1.230 2003/07/17 18:16:54 cheshire +<rdar://problem/3319418> Services always in a state of flux +In preparation for working on this, made some debugf messages a little more selective + +Revision 1.229 2003/07/17 17:35:04 cheshire +<rdar://problem/3325583> Rate-limit responses, to guard against packet flooding + +Revision 1.228 2003/07/16 20:50:27 cheshire +<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass + +Revision 1.227 2003/07/16 05:01:36 cheshire +Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for +<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass + +Revision 1.226 2003/07/16 04:51:44 cheshire +Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval' + +Revision 1.225 2003/07/16 04:46:41 cheshire +Minor wording cleanup: The correct DNS term is "response", not "reply" + +Revision 1.224 2003/07/16 04:39:02 cheshire +Textual cleanup (no change to functionality): +Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)" + +Revision 1.223 2003/07/16 00:09:22 cheshire +Textual cleanup (no change to functionality): +Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places; +replace with macro "TicksTTL(rr)" +Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" +replaced with macro "RRExpireTime(rr)" + +Revision 1.222 2003/07/15 23:40:46 cheshire +Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo() + +Revision 1.221 2003/07/15 22:17:56 cheshire +<rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries + +Revision 1.220 2003/07/15 02:12:51 cheshire +Slight tidy-up of debugf messages and comments + +Revision 1.219 2003/07/15 01:55:12 cheshire +<rdar://problem/3315777> Need to implement service registration with subtypes + +Revision 1.218 2003/07/14 16:26:06 cheshire +<rdar://problem/3324795> Duplicate query suppression not working right +Refinement: Don't record DS information for a question in the first quarter second +right after we send it -- in the case where a question happens to be accelerated by +the maximum allowed amount, we don't want it to then be suppressed because the previous +time *we* sent that question falls (just) within the valid duplicate suppression window. + +Revision 1.217 2003/07/13 04:43:53 cheshire +<rdar://problem/3325169> Services on multiple interfaces not always resolving +Minor refinement: No need to make address query broader than the original SRV query that provoked it + +Revision 1.216 2003/07/13 03:13:17 cheshire +<rdar://problem/3325169> Services on multiple interfaces not always resolving +If we get an identical SRV on a second interface, convert address queries to non-specific + +Revision 1.215 2003/07/13 02:28:00 cheshire +<rdar://problem/3325166> SendResponses didn't all its responses +Delete all references to RRInterfaceActive -- it's now superfluous + +Revision 1.214 2003/07/13 01:47:53 cheshire +Fix one error and one warning in the Windows build + +Revision 1.213 2003/07/12 04:25:48 cheshire +Fix minor signed/unsigned warnings + +Revision 1.212 2003/07/12 01:59:11 cheshire +Minor changes to debugf messages + +Revision 1.211 2003/07/12 01:47:01 cheshire +<rdar://problem/3324495> After name conflict, appended number should be higher than previous number + +Revision 1.210 2003/07/12 01:43:28 cheshire +<rdar://problem/3324795> Duplicate query suppression not working right +The correct cutoff time for duplicate query suppression is timenow less one-half the query interval. +The code was incorrectly using the last query time plus one-half the query interval. +This was only correct in the case where query acceleration was not in effect. + +Revision 1.209 2003/07/12 01:27:50 cheshire +<rdar://problem/3320079> Hostname conflict naming should not use two hyphens +Fix missing "-1" in RemoveLabelSuffix() + +Revision 1.208 2003/07/11 01:32:38 cheshire +Syntactic cleanup (no change to funcationality): Now that we only have one host name, +rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". + +Revision 1.207 2003/07/11 01:28:00 cheshire +<rdar://problem/3161289> No more local.arpa + +Revision 1.206 2003/07/11 00:45:02 cheshire +<rdar://problem/3321909> Client should get callback confirming successful host name registration + +Revision 1.205 2003/07/11 00:40:18 cheshire +Tidy up debug message in HostNameCallback() + +Revision 1.204 2003/07/11 00:20:32 cheshire +<rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes + +Revision 1.203 2003/07/10 23:53:41 cheshire +<rdar://problem/3320079> Hostname conflict naming should not use two hyphens + +Revision 1.202 2003/07/04 02:23:20 cheshire +<rdar://problem/3311955> Responder too aggressive at flushing stale data +Changed mDNSResponder to require four unanswered queries before purging a record, instead of two. + +Revision 1.201 2003/07/04 01:09:41 cheshire +<rdar://problem/3315775> Need to implement subtype queries +Modified ConstructServiceName() to allow three-part service types + +Revision 1.200 2003/07/03 23:55:26 cheshire +Minor change to wording of syslog warning messages + +Revision 1.199 2003/07/03 23:51:13 cheshire +<rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings +Added more detailed debugging information + +Revision 1.198 2003/07/03 22:19:30 cheshire +<rdar://problem/3314346> Bug fix in 3274153 breaks TiVo +Make exception to allow _tivo_servemedia._tcp. + +Revision 1.197 2003/07/02 22:33:05 cheshire +<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed +Minor refinements: +When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not +Allow cache to grow to 512 records before considering it a potential denial-of-service attack + +Revision 1.196 2003/07/02 21:19:45 cheshire +<rdar://problem/3313413> Update copyright notices, etc., in source code comments + +Revision 1.195 2003/07/02 19:56:58 cheshire +<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed +Minor refinement: m->rrcache_active was not being decremented when +an active record was deleted because its TTL expired + +Revision 1.194 2003/07/02 18:47:40 cheshire +Minor wording change to log messages + +Revision 1.193 2003/07/02 02:44:13 cheshire +Fix warning in non-debug build + +Revision 1.192 2003/07/02 02:41:23 cheshire +<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed + +Revision 1.191 2003/07/02 02:30:51 cheshire +HashSlot() returns an array index. It can't be negative; hence it should not be signed. + +Revision 1.190 2003/06/27 00:03:05 vlubet +<rdar://problem/3304625> Merge of build failure fix for gcc 3.3 + +Revision 1.189 2003/06/11 19:24:03 cheshire +<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces +Slight refinement to previous checkin + +Revision 1.188 2003/06/10 20:33:28 cheshire +<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces + +Revision 1.187 2003/06/10 04:30:44 cheshire +<rdar://problem/3286234> Need to re-probe/re-announce on configuration change +Only interface-specific records were re-probing and re-announcing, not non-specific records. + +Revision 1.186 2003/06/10 04:24:39 cheshire +<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache +Some additional refinements: +Don't try to do this for unicast-response queries +better tracking of Qs and KAs in multi-packet KA lists + +Revision 1.185 2003/06/10 03:52:49 cheshire +Update comments and debug messages + +Revision 1.184 2003/06/10 02:26:39 cheshire +<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function +Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines + +Revision 1.183 2003/06/09 18:53:13 cheshire +Simplify some debugf() statements (replaced block of 25 lines with 2 lines) + +Revision 1.182 2003/06/09 18:38:42 cheshire +<rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network +Only issue a correction if the TTL in the proxy packet is less than half the correct value. + +Revision 1.181 2003/06/07 06:45:05 cheshire +<rdar://problem/3283666> No need for multiple machines to all be sending the same queries + +Revision 1.180 2003/06/07 06:31:07 cheshire +Create little four-line helper function "FindIdenticalRecordInCache()" + +Revision 1.179 2003/06/07 06:28:13 cheshire +For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq" + +Revision 1.178 2003/06/07 06:25:12 cheshire +Update some comments + +Revision 1.177 2003/06/07 04:50:53 cheshire +<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache + +Revision 1.176 2003/06/07 04:33:26 cheshire +<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records +Minor change: Increment/decrement logic for q->CurrentAnswers should be in +CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord() + +Revision 1.175 2003/06/07 04:11:52 cheshire +Minor changes to comments and debug messages + +Revision 1.174 2003/06/07 01:46:38 cheshire +<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records + +Revision 1.173 2003/06/07 01:22:13 cheshire +<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function + +Revision 1.172 2003/06/07 00:59:42 cheshire +<rdar://problem/3283454> Need some randomness to spread queries on the network + +Revision 1.171 2003/06/06 21:41:10 cheshire +For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines + +Revision 1.170 2003/06/06 21:38:55 cheshire +Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we +already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) + +Revision 1.169 2003/06/06 21:35:55 cheshire +Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget +(the target is a domain name, but not necessarily a host name) + +Revision 1.168 2003/06/06 21:33:31 cheshire +Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval" + +Revision 1.167 2003/06/06 21:30:42 cheshire +<rdar://problem/3282962> Don't delay queries for shared record types + +Revision 1.166 2003/06/06 17:20:14 cheshire +For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass +(Global search-and-replace; no functional change to code execution.) + +Revision 1.165 2003/06/04 02:53:21 cheshire +Add some "#pragma warning" lines so it compiles clean on Microsoft compilers + +Revision 1.164 2003/06/04 01:25:33 cheshire +<rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages +Display time interval between first and subsequent queries + +Revision 1.163 2003/06/03 19:58:14 cheshire +<rdar://problem/3277665> mDNS_DeregisterService() fixes: +When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet. +Guard against a couple of possible mDNS_DeregisterService() race conditions. + +Revision 1.162 2003/06/03 19:30:39 cheshire +Minor addition refinements for +<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be + +Revision 1.161 2003/06/03 18:29:03 cheshire +Minor changes to comments and debugf() messages + +Revision 1.160 2003/06/03 05:02:16 cheshire +<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be + +Revision 1.159 2003/06/03 03:31:57 cheshire +<rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine + +Revision 1.158 2003/06/02 22:57:09 cheshire +Minor clarifying changes to comments and log messages; +IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord() + +Revision 1.157 2003/05/31 00:09:49 cheshire +<rdar://problem/3274862> Add ability to discover what services are on a network + +Revision 1.156 2003/05/30 23:56:49 cheshire +<rdar://problem/3274847> Crash after error in mDNS_RegisterService() +Need to set "sr->Extras = mDNSNULL" before returning + +Revision 1.155 2003/05/30 23:48:00 cheshire +<rdar://problem/3274832> Announcements not properly grouped +Due to inconsistent setting of rr->LastAPTime at different places in the +code, announcements were not properly grouped into a single packet. +Fixed by creating a single routine called InitializeLastAPTime(). + +Revision 1.154 2003/05/30 23:38:14 cheshire +<rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records +Wrote buffer[32] where it should have said buffer[64] + +Revision 1.153 2003/05/30 19:10:56 cheshire +<rdar://problem/3274153> ConstructServiceName needs to be more restrictive + +Revision 1.152 2003/05/29 22:39:16 cheshire +<rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character + +Revision 1.151 2003/05/29 06:35:42 cheshire +<rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record + +Revision 1.150 2003/05/29 06:25:45 cheshire +<rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion() + +Revision 1.149 2003/05/29 06:18:39 cheshire +<rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv + +Revision 1.148 2003/05/29 06:11:34 cheshire +<rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks + +Revision 1.147 2003/05/29 06:01:18 cheshire +Change some debugf() calls to LogMsg() calls to help with debugging + +Revision 1.146 2003/05/28 21:00:44 cheshire +Re-enable "immediate answer burst" debugf message + +Revision 1.145 2003/05/28 20:57:44 cheshire +<rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet +known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2 +version of mDNSResponder, so for now we should suppress this warning message. + +Revision 1.144 2003/05/28 18:05:12 cheshire +<rdar://problem/3009899> mDNSResponder allows invalid service registrations +Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names + +Revision 1.143 2003/05/28 04:31:29 cheshire +<rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time + +Revision 1.142 2003/05/28 03:13:07 cheshire +<rdar://problem/3009899> mDNSResponder allows invalid service registrations +Require that the transport protocol be _udp or _tcp + +Revision 1.141 2003/05/28 02:19:12 cheshire +<rdar://problem/3270634> Misleading messages generated by iChat +Better fix: Only generate the log message for queries where the TC bit is set. + +Revision 1.140 2003/05/28 01:55:24 cheshire +Minor change to log messages + +Revision 1.139 2003/05/28 01:52:51 cheshire +<rdar://problem/3270634> Misleading messages generated by iChat + +Revision 1.138 2003/05/27 22:35:00 cheshire +<rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions + +Revision 1.137 2003/05/27 20:04:33 cheshire +<rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf() + +Revision 1.136 2003/05/27 18:50:07 cheshire +<rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes + +Revision 1.135 2003/05/26 04:57:28 cheshire +<rdar://problem/3268953> Delay queries when there are already answers in the cache + +Revision 1.134 2003/05/26 04:54:54 cheshire +<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead +Accidentally deleted '%' case from the switch statement + +Revision 1.133 2003/05/26 03:21:27 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.132 2003/05/26 03:01:26 cheshire +<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.131 2003/05/26 00:42:05 cheshire +<rdar://problem/3268876> Temporarily include mDNSResponder version in packets + +Revision 1.130 2003/05/24 16:39:48 cheshire +<rdar://problem/3268631> SendResponses also needs to handle multihoming better + +Revision 1.129 2003/05/23 02:15:37 cheshire +Fixed misleading use of the term "duplicate suppression" where it should have +said "known answer suppression". (Duplicate answer suppression is something +different, and duplicate question suppression is yet another thing, so the use +of the completely vague term "duplicate suppression" was particularly bad.) + +Revision 1.128 2003/05/23 01:55:13 cheshire +<rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness + +Revision 1.127 2003/05/23 01:02:15 ksekar +<rdar://problem/3032577>: mDNSResponder needs to include unique id in default name + +Revision 1.126 2003/05/22 02:29:22 cheshire +<rdar://problem/2984918> SendQueries needs to handle multihoming better +Complete rewrite of SendQueries. Works much better now :-) + +Revision 1.125 2003/05/22 01:50:45 cheshire +Fix warnings, and improve log messages + +Revision 1.124 2003/05/22 01:41:50 cheshire +DiscardDeregistrations doesn't need InterfaceID parameter + +Revision 1.123 2003/05/22 01:38:55 cheshire +Change bracketing of #pragma mark + +Revision 1.122 2003/05/21 19:59:04 cheshire +<rdar://problem/3148431> ER: Tweak responder's default name conflict behavior +Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character + +Revision 1.121 2003/05/21 17:54:07 ksekar +<rdar://problem/3148431> ER: Tweak responder's default name conflict behavior +New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)" + +Revision 1.120 2003/05/19 22:14:14 ksekar +<rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type + +Revision 1.119 2003/05/16 01:34:10 cheshire +Fix some warnings + +Revision 1.118 2003/05/14 18:48:40 cheshire +<rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations +More minor refinements: +mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory +mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away + +Revision 1.117 2003/05/14 07:08:36 cheshire +<rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.116 2003/05/14 06:51:56 cheshire +<rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep + +Revision 1.115 2003/05/14 06:44:31 cheshire +Improve debugging message + +Revision 1.114 2003/05/07 01:47:03 cheshire +<rdar://problem/3250330> Also protect against NULL domainlabels + +Revision 1.113 2003/05/07 00:28:18 cheshire +<rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients + +Revision 1.112 2003/05/06 00:00:46 cheshire +<rdar://problem/3248914> Rationalize naming of domainname manipulation functions + +Revision 1.111 2003/05/05 23:42:08 cheshire +<rdar://problem/3245631> Resolves never succeed +Was setting "rr->LastAPTime = timenow - rr->LastAPTime" +instead of "rr->LastAPTime = timenow - rr->ThisAPInterval" + +Revision 1.110 2003/04/30 21:09:59 cheshire +<rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names + +Revision 1.109 2003/04/26 02:41:56 cheshire +<rdar://problem/3241281> Change timenow from a local variable to a structure member + +Revision 1.108 2003/04/25 01:45:56 cheshire +<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.107 2003/04/25 00:41:31 cheshire +<rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future + +Revision 1.106 2003/04/22 03:14:45 cheshire +<rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now + +Revision 1.105 2003/04/22 01:07:43 cheshire +<rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl +If TTL parameter is zero, leave record TTL unchanged + +Revision 1.104 2003/04/21 19:15:52 cheshire +Fix some compiler warnings + +Revision 1.103 2003/04/19 02:26:35 cheshire +<rdar://problem/3233804> Incorrect goodbye packet after conflict + +Revision 1.102 2003/04/17 03:06:28 cheshire +<rdar://problem/3231321> No need to query again when a service goes away +Set UnansweredQueries to 2 when receiving a "goodbye" packet + +Revision 1.101 2003/04/15 20:58:31 jgraessl +<rdar://problem/3229014> Added a hash to lookup records in the cache. + +Revision 1.100 2003/04/15 18:53:14 cheshire +<rdar://problem/3229064> Bug in ScheduleNextTask +mDNS.c 1.94 incorrectly combined two "if" statements into one. + +Revision 1.99 2003/04/15 18:09:13 jgraessl +<rdar://problem/3228892> +Reviewed by: Stuart Cheshire +Added code to keep track of when the next cache item will expire so we can +call TidyRRCache only when necessary. + +Revision 1.98 2003/04/03 03:43:55 cheshire +<rdar://problem/3216837> Off-by-one error in probe rate limiting + +Revision 1.97 2003/04/02 01:48:17 cheshire +<rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets +Additional fix pointed out by Josh: +Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state + +Revision 1.96 2003/04/01 23:58:55 cheshire +Minor comment changes + +Revision 1.95 2003/04/01 23:46:05 cheshire +<rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles +mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface +to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second +window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in +FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed. + +Revision 1.94 2003/03/29 01:55:19 cheshire +<rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets +Solution: Major cleanup of packet timing and conflict handling rules + +Revision 1.93 2003/03/28 01:54:36 cheshire +Minor tidyup of IPv6 (AAAA) code + +Revision 1.92 2003/03/27 03:30:55 cheshire +<rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.91 2003/03/15 04:40:36 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.90 2003/03/14 20:26:37 cheshire +Reduce debugging messages (reclassify some "debugf" as "verbosedebugf") + +Revision 1.89 2003/03/12 19:57:50 cheshire +Fixed typo in debug message + +Revision 1.88 2003/03/12 00:17:44 cheshire +<rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records + +Revision 1.87 2003/03/11 01:27:20 cheshire +Reduce debugging messages (reclassify some "debugf" as "verbosedebugf") + +Revision 1.86 2003/03/06 20:44:33 cheshire +Comment tidyup + +Revision 1.85 2003/03/05 03:38:35 cheshire +<rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found! +Fixed by leaving client in list after conflict, until client explicitly deallocates + +Revision 1.84 2003/03/05 01:27:30 cheshire +<rdar://problem/3185482> Different TTL for multicast versus unicast responses +When building unicast responses, record TTLs are capped to 10 seconds + +Revision 1.83 2003/03/04 23:48:52 cheshire +<rdar://problem/3188865> Double probes after wake from sleep +Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another + +Revision 1.82 2003/03/04 23:38:29 cheshire +<rdar://problem/3099194> mDNSResponder needs performance improvements +Only set rr->CRActiveQuestion to point to the +currently active representative of a question set + +Revision 1.81 2003/02/21 03:35:34 cheshire +<rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section + +Revision 1.80 2003/02/21 02:47:53 cheshire +<rdar://problem/3099194> mDNSResponder needs performance improvements +Several places in the code were calling CacheRRActive(), which searched the entire +question list every time, to see if this cache resource record answers any question. +Instead, we now have a field "CRActiveQuestion" in the resource record structure + +Revision 1.79 2003/02/21 01:54:07 cheshire +<rdar://problem/3099194> mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.78 2003/02/20 06:48:32 cheshire +<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations +Reviewed by: Josh Graessley, Bob Bradley + +Revision 1.77 2003/01/31 03:35:59 cheshire +<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results +When there were *two* active questions in the list, they were incorrectly +finding *each other* and *both* being marked as duplicates of another question + +Revision 1.76 2003/01/29 02:46:37 cheshire +Fix for IPv6: +A physical interface is identified solely by its InterfaceID (not by IP and type). +On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. +In cases where the requested outbound protocol (v4 or v6) is not supported on +that InterfaceID, the platform support layer should simply discard that packet. + +Revision 1.75 2003/01/29 01:47:40 cheshire +Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity + +Revision 1.74 2003/01/28 05:26:25 cheshire +<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results +Add 'Active' flag for interfaces + +Revision 1.73 2003/01/28 03:45:12 cheshire +Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)" + +Revision 1.72 2003/01/28 01:49:48 cheshire +<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results +FindDuplicateQuestion() was incorrectly finding the question itself in the list, +and incorrectly marking it as a duplicate (of itself), so that it became inactive. + +Revision 1.71 2003/01/28 01:41:44 cheshire +<rdar://problem/3153091> Race condition when network change causes bad stuff +When an interface goes away, interface-specific questions on that interface become orphaned. +Orphan questions cause HaveQueries to return true, but there's no interface to send them on. +Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions() + +Revision 1.70 2003/01/23 19:00:20 cheshire +Protect against infinite loops in mDNS_Execute + +Revision 1.69 2003/01/21 22:56:32 jgraessl +<rdar://problem/3124348> service name changes are not properly handled +Submitted by: Stuart Cheshire +Reviewed by: Joshua Graessley +Applying changes for 3124348 to main branch. 3124348 changes went in to a +branch for SU. + +Revision 1.68 2003/01/17 04:09:27 cheshire +<rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts + +Revision 1.67 2003/01/17 03:56:45 cheshire +Default 24-hour TTL is far too long. Changing to two hours. + +Revision 1.66 2003/01/13 23:49:41 jgraessl +Merged changes for the following fixes in to top of tree: +<rdar://problem/3086540> computer name changes not handled properly +<rdar://problem/3124348> service name changes are not properly handled +<rdar://problem/3124352> announcements sent in pairs, failing chattiness test + +Revision 1.65 2002/12/23 22:13:28 jgraessl +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.64 2002/11/26 20:49:06 cheshire +<rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit + +Revision 1.63 2002/09/21 20:44:49 zarzycki +Added APSL info + +Revision 1.62 2002/09/20 03:25:37 cheshire +Fix some compiler warnings + +Revision 1.61 2002/09/20 01:05:24 cheshire +Don't kill the Extras list in mDNS_DeregisterService() + +Revision 1.60 2002/09/19 23:47:35 cheshire +Added mDNS_RegisterNoSuchService() function for assertion of non-existence +of a particular named service + +Revision 1.59 2002/09/19 21:25:34 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.58 2002/09/19 04:20:43 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.57 2002/09/17 01:07:08 cheshire +Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() + +Revision 1.56 2002/09/16 19:44:17 cheshire +Merge in license terms from Quinn's copy, in preparation for Darwin release +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "DNSCommon.h" // Defines general DNS untility routines +#include "uDNS.h" // Defines entry points into unicast-specific routines +// Disable certain benign warnings with Microsoft compilers +#if(defined(_MSC_VER)) + // Disable "conditional expression is constant" warning for debug macros. + // Otherwise, this generates warnings for the perfectly natural construct "while(1)" + // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know + #pragma warning(disable:4127) + + // Disable "assignment within conditional expression". + // Other compilers understand the convention that if you place the assignment expression within an extra pair + // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. + // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal + // to the compiler that the assignment is intentional, we have to just turn this warning off completely. + #pragma warning(disable:4706) +#endif + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Program Constants +#endif + +mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; +mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; +mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; +mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } }; +mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; +mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; +mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; + +mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; +mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1; + +mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; + +#define UnicastDNSPortAsNumber 53 +#define NATPMPPortAsNumber 5351 +#define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc. +#define MulticastDNSPortAsNumber 5353 +#define LoopbackIPCPortAsNumber 5354 + +mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } }; + +mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; +mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; +mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; + +mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; +mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; +mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } }; +mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; +mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; +mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; + +// Any records bigger than this are considered 'large' records +#define SmallRecordLimit 1024 + +#define kMaxUpdateCredits 10 +#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6) + +mDNSexport const char *const mDNS_DomainTypeNames[] = + { + "b._dns-sd._udp.", // Browse + "db._dns-sd._udp.", // Default Browse + "lb._dns-sd._udp.", // Legacy Browse + "r._dns-sd._udp.", // Registration + "dr._dns-sd._udp." // Default Registration + }; + +#ifdef UNICAST_DISABLED +#define uDNS_IsActiveQuery(q, u) mDNSfalse +#endif + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Specialized mDNS version of vsnprintf +#endif + +static const struct mDNSprintf_format + { + unsigned leftJustify : 1; + unsigned forceSign : 1; + unsigned zeroPad : 1; + unsigned havePrecision : 1; + unsigned hSize : 1; + unsigned lSize : 1; + char altForm; + char sign; // +, - or space + unsigned int fieldWidth; + unsigned int precision; + } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) + { + mDNSu32 nwritten = 0; + int c; + if (buflen == 0) return(0); + buflen--; // Pre-reserve one space in the buffer for the terminating null + if (buflen == 0) goto exit; + + for (c = *fmt; c != 0; c = *++fmt) + { + if (c != '%') + { + *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + } + else + { + unsigned int i=0, j; + // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for + // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. + // The size needs to be enough for a 256-byte domain name plus some error text. + #define mDNS_VACB_Size 300 + char mDNS_VACB[mDNS_VACB_Size]; + #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) + #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s)) + char *s = mDNS_VACB_Lim, *digits; + struct mDNSprintf_format F = mDNSprintf_format_default; + + while (1) // decode flags + { + c = *++fmt; + if (c == '-') F.leftJustify = 1; + else if (c == '+') F.forceSign = 1; + else if (c == ' ') F.sign = ' '; + else if (c == '#') F.altForm++; + else if (c == '0') F.zeroPad = 1; + else break; + } + + if (c == '*') // decode field width + { + int f = va_arg(arg, int); + if (f < 0) { f = -f; F.leftJustify = 1; } + F.fieldWidth = (unsigned int)f; + c = *++fmt; + } + else + { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); + } + + if (c == '.') // decode precision + { + if ((c = *++fmt) == '*') + { F.precision = va_arg(arg, unsigned int); c = *++fmt; } + else for (; c >= '0' && c <= '9'; c = *++fmt) + F.precision = (10 * F.precision) + (c - '0'); + F.havePrecision = 1; + } + + if (F.leftJustify) F.zeroPad = 0; + + conv: + switch (c) // perform appropriate conversion + { + unsigned long n; + case 'h' : F.hSize = 1; c = *++fmt; goto conv; + case 'l' : // fall through + case 'L' : F.lSize = 1; c = *++fmt; goto conv; + case 'd' : + case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long); + else n = (unsigned long)va_arg(arg, int); + if (F.hSize) n = (short) n; + if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } + else if (F.forceSign) F.sign = '+'; + goto decimal; + case 'u' : if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + F.sign = 0; + goto decimal; + decimal: if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.sign) --F.precision; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); + for (; i < F.precision; i++) *--s = '0'; + if (F.sign) { *--s = F.sign; i++; } + break; + + case 'o' : if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + if (!F.havePrecision) + { + if (F.zeroPad) F.precision = F.fieldWidth; + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); + if (F.altForm && i && *s != '0') { *--s = '0'; i++; } + for (; i < F.precision; i++) *--s = '0'; + break; + + case 'a' : { + unsigned char *a = va_arg(arg, unsigned char *); + if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } + else + { + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (F.altForm) + { + mDNSAddr *ip = (mDNSAddr*)a; + switch (ip->type) + { + case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; + case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; + default: F.precision = 0; break; + } + } + switch (F.precision) + { + case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d", + a[0], a[1], a[2], a[3]); break; + case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", + a[0], a[1], a[2], a[3], a[4], a[5]); break; + case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), + "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], + a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; + default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify" + " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; + } + } + } + break; + + case 'p' : F.havePrecision = F.lSize = 1; + F.precision = 8; + case 'X' : digits = "0123456789ABCDEF"; + goto hexadecimal; + case 'x' : digits = "0123456789abcdef"; + hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.altForm) F.precision -= 2; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; + for (; i < F.precision; i++) *--s = '0'; + if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } + break; + + case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; + + case 's' : s = va_arg(arg, char *); + if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } + else switch (F.altForm) + { + case 0: i=0; + if (!F.havePrecision) // C string + while(s[i]) i++; + else + { + while ((i < F.precision) && s[i]) i++; + // Make sure we don't truncate in the middle of a UTF-8 character + // If last character we got was any kind of UTF-8 multi-byte character, + // then see if we have to back up. + // This is not as easy as the similar checks below, because + // here we can't assume it's safe to examine the *next* byte, so we + // have to confine ourselves to working only backwards in the string. + j = i; // Record where we got to + // Now, back up until we find first non-continuation-char + while (i>0 && (s[i-1] & 0xC0) == 0x80) i--; + // Now s[i-1] is the first non-continuation-char + // and (j-i) is the number of continuation-chars we found + if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char + { + i--; // Tentatively eliminate this start-char as well + // Now (j-i) is the number of characters we're considering eliminating. + // To be legal UTF-8, the start-char must contain (j-i) one-bits, + // followed by a zero bit. If we shift it right by (7-(j-i)) bits + // (with sign extension) then the result has to be 0xFE. + // If this is right, then we reinstate the tentatively eliminated bytes. + if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j; + } + } + break; + case 1: i = (unsigned char) *s++; break; // Pascal string + case 2: { // DNS label-sequence name + unsigned char *a = (unsigned char *)s; + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (*a == 0) *s++ = '.'; // Special case for root DNS name + while (*a) + { + if (*a > 63) + { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; } + if (s + *a >= &mDNS_VACB[254]) + { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; } + s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%#s.", a); + a += 1 + *a; + } + i = (mDNSu32)(s - mDNS_VACB); + s = mDNS_VACB; // Reset s back to the start of the buffer + break; + } + } + // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below) + if (F.havePrecision && i > F.precision) + { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } + break; + + case 'n' : s = va_arg(arg, char *); + if (F.hSize) * (short *) s = (short)nwritten; + else if (F.lSize) * (long *) s = (long)nwritten; + else * (int *) s = (int)nwritten; + continue; + + default: s = mDNS_VACB; + i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c); + + case '%' : *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + break; + } + + if (i < F.fieldWidth && !F.leftJustify) // Pad on the left + do { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } while (i < --F.fieldWidth); + + // Make sure we don't truncate in the middle of a UTF-8 character. + // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the + // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, + // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly + // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). + if (i > buflen - nwritten) + { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } + for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result + nwritten += i; + if (nwritten >= buflen) goto exit; + + for (; i < F.fieldWidth; i++) // Pad on the right + { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } + } + } + exit: + *sbuffer++ = 0; + return(nwritten); + } + +mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) + { + mDNSu32 length; + + va_list ptr; + va_start(ptr,fmt); + length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr); + va_end(ptr); + + return(length); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Utility Functions +#endif + +#define InitialQuestionInterval (mDNSPlatformOneSecond/2) +#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) +#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0) + +mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) + { + if (ActiveQuestion(q)) + if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0) + m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval); + } + +mDNSlocal CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name) + { + CacheGroup *cg; + for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) + if (cg->namehash == namehash && SameDomainName(cg->name, name)) + break; + return(cg); + } + +mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) + { + return(CacheGroupForName(m, slot, rr->namehash, rr->name)); + } + +mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) + { + NetworkInterfaceInfo *intf; + + if (addr->type == mDNSAddrType_IPv4) + { + if (addr->ip.v4.b[0] == 169 && addr->ip.v4.b[1] == 254) return(mDNStrue); + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) + if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) + return(mDNStrue); + } + + if (addr->type == mDNSAddrType_IPv6) + { + if (addr->ip.v6.b[0] == 0xFE && addr->ip.v6.b[1] == 0x80) return(mDNStrue); + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) + if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && + (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) && + (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) && + (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0)) + return(mDNStrue); + } + + return(mDNSfalse); + } + +// Set up a AuthRecord with sensible default values. +// These defaults may be overwritten with new values before mDNS_Register is called +mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, + mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) + { + mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); + // Don't try to store a TTL bigger than we can represent in platform time units + if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) + ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; + else if (ttl == 0) // And Zero TTL is illegal + ttl = DefaultTTLforRRType(rrtype); + + // Field Group 1: The actual information pertaining to this resource record + rr->resrec.RecordType = RecordType; + rr->resrec.InterfaceID = InterfaceID; + rr->resrec.name = &rr->namestorage; + rr->resrec.rrtype = rrtype; + rr->resrec.rrclass = kDNSClass_IN; + rr->resrec.rroriginalttl = ttl; +// rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal +// rr->resrec.rdestimate = set in mDNS_Register_internal +// rr->resrec.rdata = MUST be set by client + + if (RDataStorage) + rr->resrec.rdata = RDataStorage; + else + { + rr->resrec.rdata = &rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); + } + + // Field Group 2: Persistent metadata for Authoritative Records + rr->Additional1 = mDNSNULL; + rr->Additional2 = mDNSNULL; + rr->DependentOn = mDNSNULL; + rr->RRSet = mDNSNULL; + rr->RecordCallback = Callback; + rr->RecordContext = Context; + + rr->HostTarget = mDNSfalse; + rr->AllowRemoteQuery = mDNSfalse; + rr->ForceMCast = mDNSfalse; + + // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal) + + rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register() + } + +// For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord +// Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion() +mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord) + { + // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it + if (AddRecord) rr->LocalAnswer = mDNStrue; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (q->QuestionCallback) + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } + +// When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers +// to each, stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion(). +// If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any. +// Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal() +mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord) + { + if (m->CurrentQuestion) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set"); + + m->CurrentQuestion = m->LocalOnlyQuestions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again + } + + // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) + { + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again + } + } + + m->CurrentQuestion = mDNSNULL; + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Resource Record Utility Functions +#endif + +#define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA) + +#define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \ + ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ + ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ + ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) ) + +#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \ + (ResourceRecordIsValidAnswer(RR) && \ + ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) + +#define DefaultProbeCountForTypeUnique ((mDNSu8)3) +#define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) + +#define InitialAnnounceCount ((mDNSu8)10) + +// Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not. +// This means that because the announce interval is doubled after sending the first packet, the first +// observed on-the-wire inter-packet interval between announcements is actually one second. +// The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent. +#define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4) +#define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2) +#define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2) + +#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \ + (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \ + (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0) + +#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0) +#define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR)) +#define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond) +#define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR)) + +#define MaxUnansweredQueries 4 + +// SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent +// (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match). +// TTL and rdata may differ. +// This is used for cache flush management: +// When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent +// When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed +mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2) + { + if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); } + if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); } + if (r1->InterfaceID && + r2->InterfaceID && + r1->InterfaceID != r2->InterfaceID) return(mDNSfalse); + return(mDNSBool)( + r1->rrtype == r2->rrtype && + r1->rrclass == r2->rrclass && + r1->namehash == r2->namehash && + SameDomainName(r1->name, r2->name)); + } + +// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our +// authoratative record is unique (as opposed to shared). For unique records, we are supposed to have +// complete ownership of *all* types for this name, so *any* record type with the same name is a conflict. +// In addition, when probing we send our questions with the wildcard type kDNSQType_ANY, +// so a response of any type should match, even if it is not actually the type the client plans to use. +mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr) + { + if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } + if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); } + if (pktrr->resrec.InterfaceID && + authrr->resrec.InterfaceID && + pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); + if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); + return(mDNSBool)( + pktrr->resrec.rrclass == authrr->resrec.rrclass && + pktrr->resrec.namehash == authrr->resrec.namehash && + SameDomainName(pktrr->resrec.name, authrr->resrec.name)); + } + +// IdenticalResourceRecord returns true if two resources records have +// the same name, type, class, and identical rdata (InterfaceID and TTL may differ) +mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2) + { + if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); } + if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); } + if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name)) + return(mDNSfalse); + return(SameRData(r1, r2)); + } + +// CacheRecord *ks is the CacheRecord from the known answer list in the query. +// This is the information that the requester believes to be correct. +// AuthRecord *rr is the answer we are proposing to give, if not suppressed. +// This is the information that we believe to be correct. +// We've already determined that we plan to give this answer on this interface +// (either the record is non-specific, or it is specific to this interface) +// so now we just need to check the name, type, class, rdata and TTL. +mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr) + { + // If RR signature is different, or data is different, then don't suppress our answer + if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse); + + // If the requester's indicated TTL is less than half the real TTL, + // we need to give our answer before the requester's copy expires. + // If the requester's indicated TTL is at least half the real TTL, + // then we can suppress our answer this time. + // If the requester's indicated TTL is greater than the TTL we believe, + // then that's okay, and we don't need to do anything about it. + // (If two responders on the network are offering the same information, + // that's okay, and if they are offering the information with different TTLs, + // the one offering the lower TTL should defer to the one offering the higher TTL.) + return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2); + } + +mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr) + { + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + //LogMsg("ProbeCount %d Next %ld %s", + // rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); + if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); + } + else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr)) + { + if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); + } + } + +mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) + { + // To allow us to aggregate probes when a group of services are registered together, + // the first probe is delayed 1/4 second. This means the common-case behaviour is: + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) + + // If we have no probe suppression time set, or it is in the past, set it now + if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) + { + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique); + // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation + if (m->SuppressProbes - m->NextScheduledProbe >= 0) + m->SuppressProbes = m->NextScheduledProbe; + // If we already have a query scheduled to go out sooner, then use that time to get better aggregation + if (m->SuppressProbes - m->NextScheduledQuery >= 0) + m->SuppressProbes = m->NextScheduledQuery; + } + + // We announce to flush stale data from other caches. It is a reasonable assumption that any + // old stale copies will probably have the same TTL we're using, so announcing longer than + // this serves no purpose -- any stale copies of that record will have expired by then anyway. + rr->AnnounceUntil = m->timenow + TicksTTL(rr); + rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; + // Set LastMCTime to now, to inhibit multicast responses + // (no need to send additional multicast responses when we're announcing anyway) + rr->LastMCTime = m->timenow; + rr->LastMCInterface = mDNSInterfaceMark; + + // If this is a record type that's not going to probe, then delay its first announcement so that + // it will go out synchronized with the first announcement for the other records that *are* probing. + // This is a minor performance tweak that helps keep groups of related records synchronized together. + // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are + // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. + // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, + // because they will meet the criterion of being at least half-way to their scheduled announcement time. + if (rr->resrec.RecordType != kDNSRecordTypeUnique) + rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; + + SetNextAnnounceProbeTime(m, rr); + } + +#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS) + +mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) + { + domainname *target = GetRRDomainNameTarget(&rr->resrec); + + if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype); + + if (target && SameDomainName(target, &m->MulticastHostname)) + debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c); + + if (target && !SameDomainName(target, &m->MulticastHostname)) + { + AssignDomainName(target, &m->MulticastHostname); + SetNewRData(&rr->resrec, mDNSNULL, 0); + + // If we're in the middle of probing this record, we need to start again, + // because changing its rdata may change the outcome of the tie-breaker. + // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.) + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + + // If we've announced this record, we really should send a goodbye packet for the old rdata before + // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, + // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. + if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared) + debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + + rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m,rr); + } + } + +mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr) + { + if (!rr->Acknowledged && rr->RecordCallback) + { + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + rr->Acknowledged = mDNStrue; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + rr->RecordCallback(m, rr, mStatus_NoError); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } + } + +// Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified +#define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \ + ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified)) +#define RecordIsLocalDuplicate(A,B) \ + ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec)) + +mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) + { + domainname *target = GetRRDomainNameTarget(&rr->resrec); + AuthRecord *r; + AuthRecord **p = &m->ResourceRecords; + AuthRecord **d = &m->DuplicateRecords; + + mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); + + if ((mDNSs32)rr->resrec.rroriginalttl <= 0) + { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } + +#ifndef UNICAST_DISABLED + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)) + rr->uDNS_info.id = zeroID; + else return uDNS_RegisterRecord(m, rr); +#endif + + while (*p && *p != rr) p=&(*p)->next; + while (*d && *d != rr) d=&(*d)->next; + if (*d || *p) + { + LogMsg("Error! Tried to register a AuthRecord %p %##s (%s) that's already in the list", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_AlreadyRegistered); + } + + if (rr->DependentOn) + { + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + rr->resrec.RecordType = kDNSRecordTypeVerified; + else + { + LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_Invalid); + } + if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified))) + { + LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType); + return(mStatus_Invalid); + } + } + + // If this resource record is referencing a specific interface, make sure it exists + if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly) + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == rr->resrec.InterfaceID) break; + if (!intf) + { + debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID); + return(mStatus_BadReferenceErr); + } + } + + rr->next = mDNSNULL; + + // Field Group 1: Persistent metadata for Authoritative Records +// rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->Callback = already set in mDNS_SetupResourceRecord +// rr->Context = already set in mDNS_SetupResourceRecord +// rr->RecordType = already set in mDNS_SetupResourceRecord +// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client +// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client + // Make sure target is not uninitialized data, or we may crash writing debugging log messages + if (rr->HostTarget && target) target->c[0] = 0; + + // Field Group 2: Transient state for Authoritative Records + rr->Acknowledged = mDNSfalse; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; + rr->LocalAnswer = mDNSfalse; + rr->IncludeInProbe = mDNSfalse; + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; + rr->ImmedAdditional = mDNSNULL; + rr->SendRNow = mDNSNULL; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + if (!rr->HostTarget) InitializeLastAPTime(m, rr); +// rr->AnnounceUntil = Set for us in InitializeLastAPTime() +// rr->LastAPTime = Set for us in InitializeLastAPTime() +// rr->LastMCTime = Set for us in InitializeLastAPTime() +// rr->LastMCInterface = Set for us in InitializeLastAPTime() + rr->NewRData = mDNSNULL; + rr->newrdlength = 0; + rr->UpdateCallback = mDNSNULL; + rr->UpdateCredits = kMaxUpdateCredits; + rr->NextUpdateCredit = 0; + rr->UpdateBlocked = 0; + +// rr->resrec.interface = already set in mDNS_SetupResourceRecord +// rr->resrec.name->c = MUST be set by client +// rr->resrec.rrtype = already set in mDNS_SetupResourceRecord +// rr->resrec.rrclass = already set in mDNS_SetupResourceRecord +// rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord +// rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set + + if (rr->HostTarget) + SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime(); + else + { + rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); + rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue); + } + + if (!ValidateDomainName(rr->resrec.name)) + { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } + + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } + + // Don't do this until *after* we've set rr->resrec.rdlength + if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata)) + { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } + + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u); + + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) + { + // If this is supposed to be unique, make sure we don't have any name conflicts + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + { + const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; + for (r = m->ResourceRecords; r; r=r->next) + { + const AuthRecord *s2 = r->RRSet ? r->RRSet : r; + if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec)) + break; + } + if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback + { + debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + rr->resrec.rroriginalttl = 0; + rr->ImmedAnswer = mDNSInterfaceMark; + m->NextScheduledResponse = m->timenow; + } + } + } + + // Now that we've finished building our new record, make sure it's not identical to one we already have + for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break; + + if (r) + { + debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr)); + *d = rr; + // If the previous copy of this record is already verified unique, + // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. + // Setting ProbeCount to zero will cause SendQueries() to advance this record to + // kDNSRecordTypeVerified state and call the client callback at the next appropriate time. + if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified) + rr->ProbeCount = 0; + } + else + { + debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr)); + if (!m->NewLocalRecords) m->NewLocalRecords = rr; + *p = rr; + } + + // For records that are not going to probe, acknowledge them right away + if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) + AcknowledgeRecord(m, rr); + + return(mStatus_NoError); + } + +mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) + { + m->ProbeFailTime = m->timenow; + m->NumFailedProbes++; + // If we've had fifteen or more probe failures, rate-limit to one every five seconds. + // If a bunch of hosts have all been configured with the same name, then they'll all + // conflict and run through the same series of names: name-2, name-3, name-4, etc., + // up to name-10. After that they'll start adding random increments in the range 1-100, + // so they're more likely to branch out in the available namespace and settle on a set of + // unique names quickly. If after five more tries the host is still conflicting, then we + // may have a serious problem, so we start rate-limiting so we don't melt down the network. + if (m->NumFailedProbes >= 15) + { + m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); + LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect", + m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + } + } + +mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr) + { + RData *OldRData = rr->resrec.rdata; + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, OldRData); // ... and let the client know + } + +// mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal +// mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict +// mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered +typedef enum { mDNS_Dereg_normal, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type; + +// NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt) + { + AuthRecord *r2; + mDNSu8 RecordType = rr->resrec.RecordType; + AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records + +#ifndef UNICAST_DISABLED + if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name))) + return uDNS_DeregisterRecord(m, rr); +#endif + + while (*p && *p != rr) p=&(*p)->next; + + if (*p) + { + // We found our record on the main list. See if there are any duplicates that need special handling. + if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment + { + // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished + // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory. + for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF; + } + else + { + // Before we delete the record (and potentially send a goodbye packet) + // first see if we have a record on the duplicate list ready to take over from it. + AuthRecord **d = &m->DuplicateRecords; + while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next; + if (*d) + { + AuthRecord *dup = *d; + debugf("Duplicate record %p taking over from %p %##s (%s)", + dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + *d = dup->next; // Cut replacement record from DuplicateRecords list + dup->next = rr->next; // And then... + rr->next = dup; // ... splice it in right after the record we're about to delete + dup->resrec.RecordType = rr->resrec.RecordType; + dup->ProbeCount = rr->ProbeCount; + dup->AnnounceCount = rr->AnnounceCount; + dup->RequireGoodbye = rr->RequireGoodbye; + dup->ImmedAnswer = rr->ImmedAnswer; + dup->ImmedUnicast = rr->ImmedUnicast; + dup->ImmedAdditional = rr->ImmedAdditional; + dup->v4Requester = rr->v4Requester; + dup->v6Requester = rr->v6Requester; + dup->ThisAPInterval = rr->ThisAPInterval; + dup->AnnounceUntil = rr->AnnounceUntil; + dup->LastAPTime = rr->LastAPTime; + dup->LastMCTime = rr->LastMCTime; + dup->LastMCInterface = rr->LastMCInterface; + rr->RequireGoodbye = mDNSfalse; + } + } + } + else + { + // We didn't find our record on the main list; try the DuplicateRecords list instead. + p = &m->DuplicateRecords; + while (*p && *p != rr) p=&(*p)->next; + // If we found our record on the duplicate list, then make sure we don't send a goodbye for it + if (*p) rr->RequireGoodbye = mDNSfalse; + if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + } + + if (!*p) + { + // No need to log an error message if we already know this is a potentially repeated deregistration + if (drt != mDNS_Dereg_repeat) + LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_BadReferenceErr); + } + + // If this is a shared record and we've announced it at least once, + // we need to retract that announcement before we delete the record + if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + { + verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + rr->resrec.rroriginalttl = 0; + rr->ImmedAnswer = mDNSInterfaceMark; + if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0) + m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10); + } + else + { + *p = rr->next; // Cut this record from the list + // If someone is about to look at this, bump the pointer forward + if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; + if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next; + rr->next = mDNSNULL; + + if (RecordType == kDNSRecordTypeUnregistered) + debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + else if (RecordType == kDNSRecordTypeDeregistering) + debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + else + { + verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeUnregistered; + } + + if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared) + debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + + // If we have an update queued up which never executed, give the client a chance to free that memory + if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + + if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse); + + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + // In this case the likely client action to the mStatus_MemFree message is to free the memory, + // so any attempt to touch rr after this is likely to lead to a crash. + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (drt != mDNS_Dereg_conflict) + { + if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this + } + else + { + RecordProbeFailure(m, rr); + if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_NameConflict); // MUST NOT touch rr after this + // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously. + // Note that with all the client callbacks going on, by the time we get here all the + // records we marked may have been explicitly deregistered by the client anyway. + r2 = m->DuplicateRecords; + while (r2) + { + if (r2->ProbeCount != 0xFF) r2 = r2->next; + else { mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); r2 = m->DuplicateRecords; } + } + } + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } + return(mStatus_NoError); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - Packet Sending Functions +#endif + +mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add) + { + if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse) + { + **nrpp = rr; + // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo) + // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does + // The referenced record will definitely be acceptable (by recursive application of this rule) + if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo; + rr->NR_AdditionalTo = add; + *nrpp = &rr->NextResponse; + } + debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + } + +mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID) + { + AuthRecord *rr, *rr2; + for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put + { + // (Note: This is an "if", not a "while". If we add a record, we'll find it again + // later in the "for" loop, and we will follow further "additional" links then.) + if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) + AddRecordToResponseList(nrpp, rr->Additional1, rr); + + if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) + AddRecordToResponseList(nrpp, rr->Additional2, rr); + + // For SRV records, automatically add the Address record(s) for the target host + if (rr->resrec.rrtype == kDNSType_SRV) + for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records + if (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ... + ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... + rr->resrec.rdatahash == rr2->resrec.namehash && // ... whose name is the name of the SRV target + SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name)) + AddRecordToResponseList(nrpp, rr2, rr); + } + } + +mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID) + { + AuthRecord *rr; + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + + // Make a list of all our records that need to be unicast to this destination + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + // If we find we can no longer unicast this answer, clear ImmedUnicast + if (rr->ImmedAnswer == mDNSInterfaceMark || + mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) || + mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr) ) + rr->ImmedUnicast = mDNSfalse; + + if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID) + if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) || + (dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6))) + { + rr->ImmedAnswer = mDNSNULL; // Clear the state fields + rr->ImmedUnicast = mDNSfalse; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo + { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; } + } + } + + AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); + + while (ResponseRecords) + { + mDNSu8 *responseptr = m->omsg.data; + mDNSu8 *newptr; + InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); + + // Put answers in the packet + while (ResponseRecords && ResponseRecords->NR_AnswerTo) + { + rr = ResponseRecords; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (!newptr && m->omsg.h.numAnswers) break; // If packet full, send it now + if (newptr) responseptr = newptr; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + rr->RequireGoodbye = mDNStrue; + } + + // Add additionals, if there's space + while (ResponseRecords && !ResponseRecords->NR_AnswerTo) + { + rr = ResponseRecords; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + + if (newptr) responseptr = newptr; + if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue; + else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + } + + if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL); + } + } + +mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr) + { + // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() + // that it should go ahead and immediately dispose of this registration + rr->resrec.RecordType = kDNSRecordTypeShared; + rr->RequireGoodbye = mDNSfalse; + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this + } + +// NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change +// the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void DiscardDeregistrations(mDNS *const m) + { + if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + CompleteDeregistration(m, rr); // Don't touch rr after this + else + m->CurrentRecord = rr->next; + } + } + +mDNSlocal void GrantUpdateCredit(AuthRecord *rr) + { + if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; + else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); + } + +// Note about acceleration of announcements to facilitate automatic coalescing of +// multiple independent threads of announcements into a single synchronized thread: +// The announcements in the packet may be at different stages of maturity; +// One-second interval, two-second interval, four-second interval, and so on. +// After we've put in all the announcements that are due, we then consider +// whether there are other nearly-due announcements that are worth accelerating. +// To be eligible for acceleration, a record MUST NOT be older (further along +// its timeline) than the most mature record we've already put in the packet. +// In other words, younger records can have their timelines accelerated to catch up +// with their elder bretheren; this narrows the age gap and helps them eventually get in sync. +// Older records cannot have their timelines accelerated; this would just widen +// the gap between them and their younger bretheren and get them even more out of sync. + +// NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change +// the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void SendResponses(mDNS *const m) + { + int pktcount = 0; + AuthRecord *rr, *r2; + mDNSs32 maxExistingAnnounceInterval = 0; + const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + + m->NextScheduledResponse = m->timenow + 0x78000000; + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedUnicast) + { + mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} }; + mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} }; + v4.ip.v4 = rr->v4Requester; + v6.ip.v6 = rr->v6Requester; + if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer); + if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer); + if (rr->ImmedUnicast) + { + LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr)); + rr->ImmedUnicast = mDNSfalse; + } + } + + // *** + // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on + // *** + + // Run through our list of records, and decide which ones we're going to announce on all interfaces + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); + if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr)) + { + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + if (maxExistingAnnounceInterval < rr->ThisAPInterval) + maxExistingAnnounceInterval = rr->ThisAPInterval; + if (rr->UpdateBlocked) rr->UpdateBlocked = 0; + } + } + + // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one) + // Eligible records that are more than half-way to their announcement time are accelerated + for (rr = m->ResourceRecords; rr; rr=rr->next) + if ((rr->resrec.InterfaceID && rr->ImmedAnswer) || + (rr->ThisAPInterval <= maxExistingAnnounceInterval && + TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) && + ResourceRecordIsValidAnswer(rr))) + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + + // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals + // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID, + // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen) + // then all that means is that it won't get sent -- which would not be the end of the world. + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV) + for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records + if (RRTypeIsAddressType(r2->resrec.rrtype) && // For all address records (A/AAAA) ... + ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ... + rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ... + rr->resrec.rdatahash == r2->resrec.namehash && // ... whose name is the name of the SRV target + SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) && + (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID)) + r2->ImmedAdditional = r2->resrec.InterfaceID; // ... then mark this address record for sending too + + // If there's a record which is supposed to be unique that we're going to send, then make sure that we give + // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class + // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a + // record, then other RRSet members that have not been sent recently will get flushed out of client caches. + // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface + // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + { + if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked + { + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2)) + if (r2->ImmedAnswer != mDNSInterfaceMark && + r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec)) + r2->ImmedAnswer = rr->ImmedAnswer; + } + else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked + { + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2)) + if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(&r2->resrec, &rr->resrec)) + r2->ImmedAdditional = rr->ImmedAdditional; + } + } + + // Now set SendRNow state appropriately + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces + { + rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; + rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer + rr->LastMCTime = m->timenow; + rr->LastMCInterface = rr->ImmedAnswer; + // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done + if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) + { + rr->AnnounceCount--; + rr->ThisAPInterval *= 2; + rr->LastAPTime = m->timenow; + if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0; + debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount); + } + } + else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface: + { + rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface + rr->ImmedAdditional = mDNSNULL; // No need to send as additional too + rr->LastMCTime = m->timenow; + rr->LastMCInterface = rr->ImmedAnswer; + } + SetNextAnnounceProbeTime(m, rr); + //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr)); + } + + // *** + // *** 2. Loop through interface list, sending records as appropriate + // *** + + while (intf) + { + int numDereg = 0; + int numAnnounce = 0; + int numAnswer = 0; + mDNSu8 *responseptr = m->omsg.data; + mDNSu8 *newptr; + InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); + + // First Pass. Look for: + // 1. Deregistering records that need to send their goodbye packet + // 2. Updated records that need to retract their old data + // 3. Answers and announcements we need to send + // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can + // send this packet and then try again. + // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway, + // because otherwise we'll end up in an infinite loop trying to send a record that will never fit. + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->SendRNow == intf->InterfaceID) + { + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + if (!newptr && m->omsg.h.numAnswers) break; + numDereg++; + responseptr = newptr; + } + else if (rr->NewRData && !m->SleepState) // If we have new data for this record + { + RData *OldRData = rr->resrec.rdata; + mDNSu16 oldrdlength = rr->resrec.rdlength; + // See if we should send a courtesy "goodbye" for the old data before we replace it. + if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye) + { + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + if (!newptr && m->omsg.h.numAnswers) break; + numDereg++; + responseptr = newptr; + rr->RequireGoodbye = mDNSfalse; + } + // Now try to see if we can fit the update in the same packet (not fatal if we can't) + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (newptr) { responseptr = newptr; rr->RequireGoodbye = mDNStrue; } + SetNewRData(&rr->resrec, OldRData, oldrdlength); + } + else + { + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (!newptr && m->omsg.h.numAnswers) break; + rr->RequireGoodbye = (mDNSu8) (!m->SleepState); + if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; + responseptr = newptr; + } + // If sending on all interfaces, go to next interface; else we're finished now + if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any) + rr->SendRNow = GetNextActiveInterfaceID(intf); + else + rr->SendRNow = mDNSNULL; + } + + // Second Pass. Add additional records, if there's space. + newptr = responseptr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedAdditional == intf->InterfaceID) + if (ResourceRecordIsValidAnswer(rr)) + { + // If we have at least one answer already in the packet, then plan to add additionals too + mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0); + + // If we're not planning to send any additionals, but this record is a unique one, then + // make sure we haven't already sent any other members of its RRSet -- if we have, then they + // will have had the cache flush bit set, so now we need to finish the job and send the rest. + if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)) + { + const AuthRecord *a; + for (a = m->ResourceRecords; a; a=a->next) + if (a->LastMCTime == m->timenow && + a->LastMCInterface == intf->InterfaceID && + SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; } + } + if (!SendAdditional) // If we don't want to send this after all, + rr->ImmedAdditional = mDNSNULL; // then cancel its ImmedAdditional field + else if (newptr) // Else, try to add it if we can + { + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (newptr) + { + responseptr = newptr; + rr->ImmedAdditional = mDNSNULL; + rr->RequireGoodbye = mDNStrue; + // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface. + // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise, + // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get + // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches. + rr->LastMCTime = m->timenow; + rr->LastMCInterface = intf->InterfaceID; + } + } + } + + if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals) + { + debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", + numDereg, numDereg == 1 ? "" : "s", + numAnnounce, numAnnounce == 1 ? "" : "s", + numAnswer, numAnswer == 1 ? "" : "s", + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); + if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } + // There might be more things to send on this interface, so go around one more time and try again. + } + else // Nothing more to send on this interface; go to next + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + #if MDNS_DEBUGMSGS && 0 + const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p"; + debugf(msg, intf, next); + #endif + intf = next; + } + } + + // *** + // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables + // *** + + if (m->CurrentRecord) LogMsg("SendResponses: ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + + if (rr->SendRNow) + { + if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) + LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr)); + rr->SendRNow = mDNSNULL; + } + + if (rr->ImmedAnswer) + { + if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + CompleteDeregistration(m, rr); // Don't touch rr after this + else + { + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + } + } + } + verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow); + } + +// Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache, +// so we want to be lazy about how frequently we do it. +// 1. If a cache record is currently referenced by *no* active questions, +// then we don't mind expiring it up to a minute late (who will know?) +// 2. Else, if a cache record is due for some of its final expiration queries, +// we'll allow them to be late by up to 2% of the TTL +// 3. Else, if a cache record has completed all its final expiration queries without success, +// and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late +// 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets), +// so allow at most 1/10 second lateness +#define CacheCheckGracePeriod(RR) ( \ + ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \ + ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ + ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ + ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10)) + +// Note: MUST call SetNextCacheCheckTime any time we change: +// rr->TimeRcvd +// rr->resrec.rroriginalttl +// rr->UnansweredQueries +// rr->CRActiveQuestion +// Also, any time we set rr->DelayDelivery we should call SetNextCacheCheckTime to ensure m->NextCacheCheck is set if necessary +// Clearing rr->DelayDelivery does not require a call to SetNextCacheCheckTime +mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) + { + rr->NextRequiredQuery = RRExpireTime(rr); + + // If we have an active question, then see if we want to schedule a refresher query for this record. + // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL. + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + { + rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries); + rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50); + verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), + (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr)); + } + + if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0) + m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)); + + if (rr->DelayDelivery) + if (m->NextCacheCheck - rr->DelayDelivery > 0) + m->NextCacheCheck = rr->DelayDelivery; + } + +#define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) +#define kDefaultReconfirmTimeForWake ((mDNSu32)mDNSPlatformOneSecond * 5) +#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15) +#define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 30) + +mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) + { + if (interval < kMinimumReconfirmTime) + interval = kMinimumReconfirmTime; + if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below + interval = 0x10000000; + + // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration + if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3)) + { + // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts + // For all the reconfirmations in a given batch, we want to use the same random value + // so that the reconfirmation questions can be grouped into a single query packet + if (!m->RandomReconfirmDelay) m->RandomReconfirmDelay = 1 + mDNSRandom(0x3FFFFFFF); + interval += mDNSRandomFromFixedSeed(m->RandomReconfirmDelay, interval/3); + rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; + rr->resrec.rroriginalttl = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond; + SetNextCacheCheckTime(m, rr); + } + debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr)); + return(mStatus_NoError); + } + +#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) + +// BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr. +// It also appends to the list of known answer records that need to be included, +// and updates the forcast for the size of the known answer section. +mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, + CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) + { + mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; + mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); + const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; + mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); + if (!newptr) + { + debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c); + return(mDNSfalse); + } + else if (newptr + *answerforecast >= limit) + { + verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", + q->qname.c, newptr + *answerforecast - query->data); + query->h.numQuestions--; + return(mDNSfalse); + } + else + { + mDNSu32 forecast = *answerforecast; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rr; + CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, + if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list + rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow > // and its half-way-to-expiry time is at least 1 second away + mDNSPlatformOneSecond) // (also ensures we never include goodbye records with TTL=1) + { + *ka = rr; // Link this record into our known answer chain + ka = &rr->NextInKAList; + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + forecast += 12 + rr->resrec.rdestimate; + // If we're trying to put more than one question in this packet, and it doesn't fit + // then undo that last question and try again next time + if (query->h.numQuestions > 1 && newptr + forecast >= limit) + { + debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", + q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data); + query->h.numQuestions--; + ka = *kalistptrptr; // Go back to where we started and retract these answer records + while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; } + return(mDNSfalse); // Return false, so we'll try again in the next packet + } + } + + // Traffic reduction: + // If we already have at least one unique answer in the cache, + // OR we have so many shared answers that the KA list is too big to fit in one packet + // The we suppress queries number 3 and 5: + // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies) + // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally) + // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress) + // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally) + // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress) + // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally) + if (q->UniqueAnswers || newptr + forecast >= limit) + if (q->ThisQInterval == InitialQuestionInterval * 8 || q->ThisQInterval == InitialQuestionInterval * 32) + { + query->h.numQuestions--; + ka = *kalistptrptr; // Go back to where we started and retract these answer records + while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; } + return(mDNStrue); // Return true: pretend we succeeded, even though we actually suppressed this question + } + + // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return + *queryptr = newptr; // Update the packet pointer + *answerforecast = forecast; // Update the forecast + *kalistptrptr = ka; // Update the known answer list pointer + if (ucast) m->ExpectUnicastResponse = m->timenow; + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // For every resource record in our cache, + if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list + ResourceRecordAnswersQuestion(&rr->resrec, q)) // which answers our question + { + rr->UnansweredQueries++; // indicate that we're expecting a response + rr->LastUnansweredTime = m->timenow; + SetNextCacheCheckTime(m, rr); + } + + return(mDNStrue); + } + } + +mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q) + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + domainname *target; + FORALL_CACHERECORDS(slot, cg, rr) + if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdatahash == q->qnamehash && SameDomainName(target, &q->qname)) + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); + } + +// Only DupSuppressInfos newer than the specified 'time' are allowed to remain active +mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time) + { + int i; + for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL; + } + +mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID) + { + int i; + for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL; + } + +mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf) + { + int i; + mDNSBool v4 = !intf->IPv4Available; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query + mDNSBool v6 = !intf->IPv6Available; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query + for (i=0; i<DupSuppressInfoSize; i++) + if (ds[i].InterfaceID == intf->InterfaceID) + { + if (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue; + else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue; + if (v4 && v6) return(mDNStrue); + } + return(mDNSfalse); + } + +mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) + { + int i, j; + + // See if we have this one in our list somewhere already + for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break; + + // If not, find a slot we can re-use + if (i >= DupSuppressInfoSize) + { + i = 0; + for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++) + if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0) + i = j; + } + + // Record the info about this query we saw + ds[i].Time = Time; + ds[i].InterfaceID = InterfaceID; + ds[i].Type = Type; + + return(i); + } + +mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) + { + // If more than 90% of the way to the query time, we should unconditionally accelerate it + if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10)) + return(mDNStrue); + + // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet + if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2)) + { + // We forecast: qname (n) type (2) class (2) + mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, + if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry + rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery + { + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + forecast += 12 + rr->resrec.rdestimate; + if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate + } + return(mDNStrue); + } + + return(mDNSfalse); + } + +// How Standard Queries are generated: +// 1. The Question Section contains the question +// 2. The Additional Section contains answers we already know, to suppress duplicate responses + +// How Probe Queries are generated: +// 1. The Question Section contains queries for the name we intend to use, with QType=ANY because +// if some other host is already using *any* records with this name, we want to know about it. +// 2. The Authority Section contains the proposed values we intend to use for one or more +// of our records with that name (analogous to the Update section of DNS Update packets) +// because if some other host is probing at the same time, we each want to know what the other is +// planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't. + +mDNSlocal void SendQueries(mDNS *const m) + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + AuthRecord *ar; + int pktcount = 0; + DNSQuestion *q; + // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval + mDNSs32 maxExistingQuestionInterval = 0; + const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + CacheRecord *KnownAnswerList = mDNSNULL; + + // 1. If time for a query, work out what we need to do + if (m->timenow - m->NextScheduledQuery >= 0) + { + CacheRecord *rr; + m->NextScheduledQuery = m->timenow + 0x78000000; + + // We're expecting to send a query anyway, so see if any expiring cache records are close enough + // to their NextRequiredQuery to be worth batching them together with this one + FORALL_CACHERECORDS(slot, cg, rr) + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0) + { + q = rr->CRActiveQuestion; + ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID); + if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If unicast query, mark it + else if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID; + else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark; + } + + // Scan our list of questions to see which *unicast* queries need to be sent + for (q = m->Questions; q; q=q->next) + if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) + { + mDNSu8 *qptr = m->omsg.data; + const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); + InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); + qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); + mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, &q->Target, q->TargetPort, -1, mDNSNULL); + q->ThisQInterval *= 2; + if (q->ThisQInterval > MaxQuestionInterval) + q->ThisQInterval = MaxQuestionInterval; + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->SendQNow = mDNSNULL; + m->ExpectUnicastResponse = m->timenow; + } + + // Scan our list of questions to see which *multicast* queries we're definitely going to send + for (q = m->Questions; q; q=q->next) + if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow)) + { + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + if (maxExistingQuestionInterval < q->ThisQInterval) + maxExistingQuestionInterval = q->ThisQInterval; + } + + // Scan our list of questions + // (a) to see if there are any more that are worth accelerating, and + // (b) to update the state variables for *all* the questions we're going to send + for (q = m->Questions; q; q=q->next) + { + if (q->SendQNow || + (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q))) + { + // If at least halfway to next query time, advance to next interval + // If less than halfway to next query time, then + // treat this as logically a repeat of the last transmission, without advancing the interval + if (m->timenow - (q->LastQTime + q->ThisQInterval/2) >= 0) + { + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + q->ThisQInterval *= 2; + if (q->ThisQInterval > MaxQuestionInterval) + q->ThisQInterval = MaxQuestionInterval; + else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * 8) + { + debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", + q->qname.c, DNSTypeName(q->qtype)); + ReconfirmAntecedents(m, q); // Sending third query, and no answers yet; time to begin doubting the source + } + } + + // Mark for sending. (If no active interfaces, then don't even try.) + q->SendOnAll = (q->SendQNow == mDNSInterfaceMark); + if (q->SendOnAll) + { + q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID; + q->LastQTime = m->timenow; + } + + // If we recorded a duplicate suppression for this question less than half an interval ago, + // then we consider it recent enough that we don't need to do an identical query ourselves. + ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); + + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + if (q->RequestUnicast) q->RequestUnicast--; + } + // For all questions (not just the ones we're sending) check what the next scheduled event will be + SetNextQueryTime(m,q); + } + } + + // 2. Scan our authoritative RR list to see what probes we might need to send + if (m->timenow - m->NextScheduledProbe >= 0) + { + m->NextScheduledProbe = m->timenow + 0x78000000; + + if (m->CurrentRecord) LogMsg("SendQueries: ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing... + { + // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly + if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) + { + SetNextAnnounceProbeTime(m, rr); + } + // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly + else if (rr->ProbeCount) + { + // Mark for sending. (If no active interfaces, then don't even try.) + rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; + rr->LastAPTime = m->timenow; + rr->ProbeCount--; + SetNextAnnounceProbeTime(m, rr); + } + // else, if it has now finished probing, move it to state Verified, + // and update m->NextScheduledResponse so it will be announced + else + { + AuthRecord *r2; + rr->resrec.RecordType = kDNSRecordTypeVerified; + rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; + rr->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; + SetNextAnnounceProbeTime(m, rr); + // If we have any records on our duplicate list that match this one, they have now also completed probing + for (r2 = m->DuplicateRecords; r2; r2=r2->next) + if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr)) + r2->ProbeCount = 0; + AcknowledgeRecord(m, rr); + } + } + } + m->CurrentRecord = m->DuplicateRecords; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0) + AcknowledgeRecord(m, rr); + } + } + + // 3. Now we know which queries and probes we're sending, + // go through our interface list sending the appropriate queries on each interface + while (intf) + { + AuthRecord *rr; + mDNSu8 *queryptr = m->omsg.data; + InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); + if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); + if (!KnownAnswerList) + { + // Start a new known-answer list + CacheRecord **kalistptr = &KnownAnswerList; + mDNSu32 answerforecast = 0; + + // Put query questions in this packet + for (q = m->Questions; q; q=q->next) + if (q->SendQNow == intf->InterfaceID) + { + debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d", + SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ", + q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data); + // If we're suppressing this question, or we successfully put it, update its SendQNow state + if (SuppressOnThisInterface(q->DupSuppress, intf) || + BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) + q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); + } + + // Put probe questions in this packet + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->SendRNow == intf->InterfaceID) + { + mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; + mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); + const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate; + if (newptr && newptr + forecast < limit) + { + queryptr = newptr; + answerforecast = forecast; + rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + rr->IncludeInProbe = mDNStrue; + verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount); + } + else + { + verbosedebugf("SendQueries: Retracting Question %##s (%s)", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + m->omsg.h.numQuestions--; + } + } + } + + // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) + while (KnownAnswerList) + { + CacheRecord *rr = KnownAnswerList; + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; + mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); + if (newptr) + { + verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); + queryptr = newptr; + KnownAnswerList = rr->NextInKAList; + rr->NextInKAList = mDNSNULL; + } + else + { + // If we ran out of space and we have more than one question in the packet, that's an error -- + // we shouldn't have put more than one question if there was a risk of us running out of space. + if (m->omsg.h.numQuestions > 1) + LogMsg("SendQueries: Put %d answers; No more space for known answers", m->omsg.h.numAnswers); + m->omsg.h.flags.b[0] |= kDNSFlag0_TC; + break; + } + } + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->IncludeInProbe) + { + mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec); + rr->IncludeInProbe = mDNSfalse; + if (newptr) queryptr = newptr; + else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + } + + if (queryptr > m->omsg.data) + { + if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) + LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); + debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", + m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", + m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", + m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); + if (++pktcount >= 1000) + { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; } + // There might be more records left in the known answer list, or more questions to send + // on this interface, so go around one more time and try again. + } + else // Nothing more to send on this interface; go to next + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + #if MDNS_DEBUGMSGS && 0 + const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p"; + debugf(msg, intf, next); + #endif + intf = next; + } + } + + // 4. Final housekeeping + + // 4a. Debugging check: Make sure we announced all our records + for (ar = m->ResourceRecords; ar; ar=ar->next) + if (ar->SendRNow) + { + if (ar->resrec.InterfaceID != mDNSInterface_LocalOnly) + LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, ar)); + ar->SendRNow = mDNSNULL; + } + + // 4b. When we have lingering cache records that we're keeping around for a few seconds in the hope + // that their interface which went away might come back again, the logic will want to send queries + // for those records, but we can't because their interface isn't here any more, so to keep the + // state machine ticking over we just pretend we did so. + // If the interface does not come back in time, the cache record will expire naturally + FORALL_CACHERECORDS(slot, cg, cr) + if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries && m->timenow - cr->NextRequiredQuery >= 0) + { + cr->UnansweredQueries++; + cr->CRActiveQuestion->SendQNow = mDNSNULL; + SetNextCacheCheckTime(m, cr); + } + + // 4c. Debugging check: Make sure we sent all our planned questions + // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions + // we legitimately couldn't send because the interface is no longer available + for (q = m->Questions; q; q=q->next) + if (q->SendQNow) + { + LogMsg("SendQueries: No active interface to send: %##s %s", q->qname.c, DNSTypeName(q->qtype)); + q->SendQNow = mDNSNULL; + } + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - RR List Management & Task Management +#endif + +// NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord) + { + verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)", + q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + + // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue) + // may be called twice, once when the record is received, and again when it's time to notify local clients. + // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this. + + rr->LastUsed = m->timenow; + if (ActiveQuestion(q) && rr->CRActiveQuestion != q) + { + if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count + rr->CRActiveQuestion = q; // We know q is non-null + SetNextCacheCheckTime(m, rr); + } + + // If this is: + // (a) a no-cache add, where we've already done at least one 'QM' query, or + // (b) a normal add, where we have at least one unique-type answer, + // then there's no need to keep polling the network. + // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.) + if ((AddRecord == 2 && !q->RequestUnicast) || + (AddRecord == 1 && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)))) + if (ActiveQuestion(q)) + { + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->ThisQInterval = MaxQuestionInterval; + q->RequestUnicast = mDNSfalse; + } + + if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (q->QuestionCallback) + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv() + // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions + // being deleted out from under them. + } + +mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) + { + rr->DelayDelivery = 0; // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared + if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); + } + m->CurrentQuestion = mDNSNULL; + } + +mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot) + { + const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second + const mDNSs32 start = m->timenow - 0x10000000; + mDNSs32 delay = start; + CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); + CacheRecord *rr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (rr->resrec.namehash == namehash && SameDomainName(rr->resrec.name, name)) + if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second + if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted + delay = RRExpireTime(rr); + if (delay - start > 0) return(NonZeroTime(delay)); + else return(0); + } + +// CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. +// If new questions are created as a result of invoking client callbacks, they will be added to +// the end of the question list, and m->NewQuestions will be set to indicate the first new question. +// rr is a new CacheRecord just received into our cache +// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). +// NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback, +// which may change the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) + { + if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + // If this question is one that's actively sending queries, and it's received ten answers within one + // second of sending the last query packet, then that indicates some radical network topology change, + // so reset its exponential backoff back to the start. We must be at least at the eight-second interval + // to do this. If we're at the four-second interval, or less, there's not much benefit accelerating + // because we will anyway send another query within a few seconds. The first reset query is sent out + // randomized over the next four seconds to reduce possible synchronization between machines. + if (q->LastAnswerPktNum != m->PktNum) + { + q->LastAnswerPktNum = m->PktNum; + if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 && + q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) + { + LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence", + q->qname.c, DNSTypeName(q->qtype)); + q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); + q->ThisQInterval = InitialQuestionInterval; + SetNextQueryTime(m,q); + } + } + verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", + rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); + q->CurrentAnswers++; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + if (q->CurrentAnswers > 4000) + { + static int msgcount = 0; + if (msgcount++ < 10) + LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack", + q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers); + rr->resrec.rroriginalttl = 1; + rr->UnansweredQueries = MaxUnansweredQueries; + } + AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); + // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() + } + } + m->CurrentQuestion = mDNSNULL; + SetNextCacheCheckTime(m, rr); + } + +// NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. +// If new questions are created as a result of invoking client callbacks, they will be added to +// the end of the question list, and m->NewQuestions will be set to indicate the first new question. +// rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique) +// but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any +// way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists, +// so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network. +// NOTE: NoCacheAnswer calls AnswerQuestionWithResourceRecord which can call a user callback, +// which may change the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) + { + LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c); + if (m->CurrentQuestion) LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerQuestionWithResourceRecord(m, q, rr, 2); // Value '2' indicates "don't expect 'remove' events for this" + // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() + } + m->CurrentQuestion = mDNSNULL; + } + +// CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute +// If new questions are created as a result of invoking client callbacks, they will be added to +// the end of the question list, and m->NewQuestions will be set to indicate the first new question. +// rr is an existing cache CacheRecord that just expired and is being deleted +// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). +// NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback, +// which may change the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) + { + if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr)); + if (q->CurrentAnswers == 0) + LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", + q, q->qname.c, DNSTypeName(q->qtype)); + else + { + q->CurrentAnswers--; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + } + if (q->CurrentAnswers == 0) + { + debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", + q->qname.c, DNSTypeName(q->qtype)); + ReconfirmAntecedents(m, q); + } + q->FlappingInterface = mDNSNULL; + AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse); + // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() + } + } + m->CurrentQuestion = mDNSNULL; + } + +mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e) + { +#if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + unsigned int i; + for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF; +#endif + e->next = m->rrcache_free; + m->rrcache_free = e; + m->rrcache_totalused--; + } + +mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp) + { + CacheEntity *e = (CacheEntity *)(*cp); + //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c); + if ((*cp)->rrcache_tail != &(*cp)->members) + LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)"); + //if ((*cp)->name != (domainname*)((*cp)->namestorage)) + // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); + if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); + (*cp)->name = mDNSNULL; + *cp = (*cp)->next; // Cut record from list + ReleaseCacheEntity(m, e); + } + +mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) + { + if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage) mDNSPlatformMemFree(r->resrec.rdata); + r->resrec.rdata = mDNSNULL; + ReleaseCacheEntity(m, (CacheEntity *)r); + } + +// Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering +// CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all +// callbacks for old records are delivered before callbacks for newer records. +mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *cg) + { + CacheRecord **rp = &cg->members; + + if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; } + m->lock_rrcache = 1; + + while (*rp) + { + CacheRecord *const rr = *rp; + mDNSs32 event = RRExpireTime(rr); + if (m->timenow - event >= 0) // If expired, delete it + { + *rp = rr->next; // Cut it from the list + verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr)); + if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away + { + CacheRecordRmv(m, rr); + m->rrcache_active--; + } + ReleaseCacheRecord(m, rr); + } + else // else, not expired; see if we need to query + { + if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) + event = rr->DelayDelivery; + else + { + if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr); + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + { + if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query + event = rr->NextRequiredQuery; // then just record when we want the next query + else // else trigger our question to go out now + { + // Set NextScheduledQuery to timenow so that SendQueries() will run. + // SendQueries() will see that we have records close to expiration, and send FEQs for them. + m->NextScheduledQuery = m->timenow; + // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), + // which will correctly update m->NextCacheCheck for us. + event = m->timenow + 0x3FFFFFFF; + } + } + } + verbosedebugf("CheckCacheExpiration:%6d %5d %s", + (event-m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr)); + if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0) + m->NextCacheCheck = (event + CacheCheckGracePeriod(rr)); + rp = &rr->next; + } + } + if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp); + cg->rrcache_tail = rp; + m->lock_rrcache = 0; + } + +mDNSlocal void AnswerNewQuestion(mDNS *const m) + { + mDNSBool ShouldQueryImmediately = mDNStrue; + CacheRecord *rr; + DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + + verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (cg) CheckCacheExpiration(m, cg); + m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration(); + + if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!"); + // This should be safe, because calling the client's question callback may cause the + // question list to be modified, but should not ever cause the rrcache list to be modified. + // If the client's question callback deletes the question, then m->CurrentQuestion will + // be advanced, and we'll exit out of the loop + m->lock_rrcache = 1; + if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted + + if (q->InterfaceID == mDNSInterface_Any) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records + { + if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue); + // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } + m->CurrentRecord = mDNSNULL; + } + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + // SecsSinceRcvd is whole number of elapsed seconds, rounded down + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; + if (rr->resrec.rroriginalttl <= SecsSinceRcvd) + { + LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)", + rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + continue; // Go to next one in loop + } + + // If this record set is marked unique, then that means we can reasonably assume we have the whole set + // -- we don't need to rush out on the network and query immediately to see if there are more answers out there + if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) + ShouldQueryImmediately = mDNSfalse; + q->CurrentAnswers++; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); + // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) + if (rr->resrec.namehash == q->qnamehash && SameDomainName(rr->resrec.name, &q->qname)) + ShouldQueryImmediately = mDNSfalse; + + if (ShouldQueryImmediately && m->CurrentQuestion == q) + { + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + m->NextScheduledQuery = m->timenow; + } + m->CurrentQuestion = mDNSNULL; + m->lock_rrcache = 0; + } + +// When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any +// appropriate answers, stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions +mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) + { + DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer + m->NewLocalOnlyQuestions = q->next; // Advance NewQuestions to the next (if any) + + debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted + + if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue); + // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } + + m->CurrentQuestion = mDNSNULL; + m->CurrentRecord = mDNSNULL; + } + +mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG) + { + CacheEntity *e = mDNSNULL; + + if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } + m->lock_rrcache = 1; + + // If we have no free records, ask the client layer to give us some more memory + if (!m->rrcache_free && m->MainCallback) + { + if (m->rrcache_totalused != m->rrcache_size) + LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu", + m->rrcache_totalused, m->rrcache_size); + + // We don't want to be vulnerable to a malicious attacker flooding us with an infinite + // number of bogus records so that we keep growing our cache until the machine runs out of memory. + // To guard against this, if we're actively using less than 1/32 of our cache, then we + // purge all the unused records and recycle them, instead of allocating more memory. + if (m->rrcache_size >= 512 && m->rrcache_size / 32 > m->rrcache_active) + debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", + m->rrcache_size, m->rrcache_active); + else + { + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + m->MainCallback(m, mStatus_GrowCache); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } + } + + // If we still have no free records, recycle all the records we can. + // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass. + if (!m->rrcache_free) + { + #if MDNS_DEBUGMSGS + mDNSu32 oldtotalused = m->rrcache_totalused; + #endif + mDNSu32 slot; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + CacheGroup **cp = &m->rrcache_hash[slot]; + while (*cp) + { + CacheRecord **rp = &(*cp)->members; + while (*rp) + { + // Records that answer still-active questions are not candidates for recycling + // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash + if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList) + rp=&(*rp)->next; + else + { + CacheRecord *rr = *rp; + *rp = (*rp)->next; // Cut record from list + ReleaseCacheRecord(m, rr); + } + } + if ((*cp)->rrcache_tail != rp) + verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp); + (*cp)->rrcache_tail = rp; + if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next; + else ReleaseCacheGroup(m, cp); + } + } + #if MDNS_DEBUGMSGS + debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused); + #endif + } + + if (m->rrcache_free) // If there are records in the free list, take one + { + e = m->rrcache_free; + m->rrcache_free = e->next; + if (++m->rrcache_totalused >= m->rrcache_report) + { + debugf("RR Cache now using %ld objects", m->rrcache_totalused); + if (m->rrcache_report < 100) m->rrcache_report += 10; + else m->rrcache_report += 100; + } + mDNSPlatformMemZero(e, sizeof(*e)); + } + + m->lock_rrcache = 0; + + return(e); + } + +mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength) + { + CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg); + if (r) + { + r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage + if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage + { + r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); + if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; + else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; } + } + } + return(r); + } + +mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr) + { + mDNSu16 namelen = DomainNameLength(rr->name); + CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL); + if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); } + cg->next = m->rrcache_hash[slot]; + cg->namehash = rr->namehash; + cg->members = mDNSNULL; + cg->rrcache_tail = &cg->members; + cg->name = (domainname*)cg->namestorage; + //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", + // (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c); + if (namelen > InlineCacheGroupNameSize) cg->name = mDNSPlatformMemAllocate(namelen); + if (!cg->name) + { + LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c); + ReleaseCacheEntity(m, (CacheEntity*)cg); + return(mDNSNULL); + } + AssignDomainName(cg->name, rr->name); + + if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c); + m->rrcache_hash[slot] = cg; + if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c); + + return(cg); + } + +mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) + { + // Make sure we mark this record as thoroughly expired -- we don't ever want to give + // a positive answer using an expired record (e.g. from an interface that has gone away). + // We don't want to clear CRActiveQuestion here, because that would leave the record subject to + // summary deletion without giving the proper callback to any questions that are monitoring it. + // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries. + rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60; + rr->UnansweredQueries = MaxUnansweredQueries; + rr->resrec.rroriginalttl = 0; + SetNextCacheCheckTime(m, rr); + } + +mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) + { + mDNSs32 time; + mDNSPlatformLock(m); + if (m->mDNS_busy) + { + LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow."); + if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); + } + + if (m->timenow) time = m->timenow; + else time = mDNS_TimeNow_NoLock(m); + mDNSPlatformUnlock(m); + return(time); + } + +mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) + { + mDNS_Lock(m); // Must grab lock before trying to read m->timenow + + if (m->timenow - m->NextScheduledEvent >= 0) + { + int i; + + verbosedebugf("mDNS_Execute"); + if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set"); + + // 1. If we're past the probe suppression time, we can clear it + if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0; + + // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter + if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0; + + // 3. Purge our cache of stale old records + if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0) + { + mDNSu32 slot; + m->NextCacheCheck = m->timenow + 0x3FFFFFFF; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + CacheGroup **cp = &m->rrcache_hash[slot]; + while (*cp) + { + CheckCacheExpiration(m, *cp); + if ((*cp)->members) cp=&(*cp)->next; + else ReleaseCacheGroup(m, cp); + } + } + LogOperation("Cache checked. Next in %ld ticks", m->NextCacheCheck - m->timenow); + } + + // 4. See if we can answer any of our new local questions from the cache + for (i=0; m->NewQuestions && i<1000; i++) + { + if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break; + AnswerNewQuestion(m); + } + if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); + + for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); + if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); + + for (i=0; i<1000 && m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords); i++) + { + AuthRecord *rr = m->NewLocalRecords; + m->NewLocalRecords = m->NewLocalRecords->next; + AnswerLocalQuestions(m, rr, mDNStrue); + } + if (i >= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit"); + + // 5. See what packets we need to send + if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) DiscardDeregistrations(m); + else if (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0) + { + // If the platform code is ready, and we're not suppressing packet generation right now + // then send our responses, probes, and questions. + // We check the cache first, because there might be records close to expiring that trigger questions to refresh them. + // We send queries next, because there might be final-stage probes that complete their probing here, causing + // them to advance to announcing state, and we want those to be included in any announcements we send out. + // Finally, we send responses, including the previously mentioned records that just completed probing. + m->SuppressSending = 0; + + // 6. Send Query packets. This may cause some probing records to advance to announcing state + if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m); + if (m->timenow - m->NextScheduledQuery >= 0) + { + LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second"); + m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond; + } + if (m->timenow - m->NextScheduledProbe >= 0) + { + LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second"); + m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond; + } + + // 7. Send Response packets, including probing records just advanced to announcing state + if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m); + if (m->timenow - m->NextScheduledResponse >= 0) + { + LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second"); + m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond; + } + } + + // Clear RandomDelay values, ready to pick a new different value next time + m->RandomQueryDelay = 0; + m->RandomReconfirmDelay = 0; + } + + // Note about multi-threaded systems: + // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(), + // performing mDNS API operations that change our next scheduled event time. + // + // On multi-threaded systems (like the current Windows implementation) that have a single main thread + // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility + // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will + // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one + // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful + // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it + // does, the state of the signal will be noticed, causing the blocking primitive to return immediately + // without blocking. This avoids the race condition between the signal from the other thread arriving + // just *before* or just *after* the main thread enters the blocking primitive. + // + // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven, + // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to + // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer + // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale + // by the time it gets to the timer callback function). + +#ifndef UNICAST_DISABLED + uDNS_Execute(m); +#endif + mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value + return(m->NextScheduledEvent); + } + +// Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep. +// Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up. +// Normally, the platform support layer below mDNSCore should call this, not the client layer above. +// Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call +// mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just +// found itself in a new network environment. For example, if the Ethernet hardware indicates that the +// cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse) +// to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc. +// While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network +// traffic, so it should only be called when there is legitimate reason to believe the machine +// may have become attached to a new network. +mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) + { + AuthRecord *rr; + + mDNS_Lock(m); + + m->SleepState = sleepstate; + LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow); + + if (sleepstate) + { +#ifndef UNICAST_DISABLED + uDNS_Sleep(m); +#endif + // Mark all the records we need to deregister and send them + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + rr->ImmedAnswer = mDNSInterfaceMark; + SendResponses(m); + } + else + { + DNSQuestion *q; + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + +#ifndef UNICAST_DISABLED + uDNS_Wake(m); +#endif + // 1. Retrigger all our questions + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (ActiveQuestion(q)) + { + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswerPkts = 0; + ExpireDupSuppressInfo(q->DupSuppress, m->timenow); + m->NextScheduledQuery = m->timenow; + } + + // 2. Re-validate our cache records + m->NextCacheCheck = m->timenow; + FORALL_CACHERECORDS(slot, cg, cr) + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake); + + // 3. Retrigger probing and announcing for all our authoritative records + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m, rr); + } + } + + mDNS_Unlock(m); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Packet Reception Functions +#endif + +#define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo) + +mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end, + const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords) + { + mDNSu8 *responseptr = response->data; + const mDNSu8 *const limit = response->data + sizeof(response->data); + const mDNSu8 *ptr = query->data; + AuthRecord *rr; + mDNSu32 maxttl = 0x70000000; + int i; + + // Initialize the response fields so we can answer the questions + InitializeDNSMessage(&response->h, query->h.id, ResponseFlags); + + // *** + // *** 1. Write out the list of questions we are actually going to answer with this packet + // *** + if (LegacyQuery) + { + maxttl = 10; + for (i=0; i<query->h.numQuestions; i++) // For each question... + { + DNSQuestion q; + ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question... + if (!ptr) return(mDNSNULL); + + for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers + { + if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question + { // then put the question in the question section + responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass); + if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); } + break; // break out of the ResponseRecords loop, and go on to the next question + } + } + } + + if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); } + } + + // *** + // *** 2. Write Answers + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AnswerTo) + { + mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, maxttl); + if (p) responseptr = p; + else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; } + } + + // *** + // *** 3. Write Additionals + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AdditionalTo && !rr->NR_AnswerTo) + { + mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, maxttl); + if (p) responseptr = p; + else debugf("GenerateUnicastResponse: No more space for additionals"); + } + + return(responseptr); + } + +// AuthRecord *our is our Resource Record +// CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network +// Returns 0 if there is no conflict +// Returns +1 if there was a conflict and we won +// Returns -1 if there was a conflict and we lost and have to rename +mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt) + { + mDNSu8 ourdata[256], *ourptr = ourdata, *ourend; + mDNSu8 pktdata[256], *pktptr = pktdata, *pktend; + if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); } + if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); } + + ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec); + pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec); + while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; } + if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict + + if (ourptr >= ourend) return(-1); // Our data ran out first; We lost + if (pktptr >= pktend) return(+1); // Packet data ran out first; We won + if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost + if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won + + LogMsg("CompareRData ERROR: Invalid state"); + return(-1); + } + +// See if we have an authoritative record that's identical to this packet record, +// whose canonical DependentOn record is the specified master record. +// The DependentOn pointer is typically used for the TXT record of service registrations +// It indicates that there is no inherent conflict detection for the TXT record +// -- it depends on the SRV record to resolve name conflicts +// If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn +// pointer chain (if any) to make sure we reach the canonical DependentOn record +// If the record has no DependentOn, then just return that record's pointer +// Returns NULL if we don't have any local RRs that are identical to the one from the packet +mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master) + { + const AuthRecord *r1; + for (r1 = m->ResourceRecords; r1; r1=r1->next) + { + if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) + { + const AuthRecord *r2 = r1; + while (r2->DependentOn) r2 = r2->DependentOn; + if (r2 == master) return(mDNStrue); + } + } + for (r1 = m->DuplicateRecords; r1; r1=r1->next) + { + if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) + { + const AuthRecord *r2 = r1; + while (r2->DependentOn) r2 = r2->DependentOn; + if (r2 == master) return(mDNStrue); + } + } + return(mDNSfalse); + } + +// Find the canonical RRSet pointer for this RR received in a packet. +// If we find any identical AuthRecord in our authoritative list, then follow its RRSet +// pointers (if any) to make sure we return the canonical member of this name/type/class +// Returns NULL if we don't have any local RRs that are identical to the one from the packet +mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr) + { + const AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec)) + { + while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet; + return(rr); + } + } + return(mDNSNULL); + } + +// PacketRRConflict is called when we've received an RR (pktrr) which has the same name +// as one of our records (our) but different rdata. +// 1. If our record is not a type that's supposed to be unique, we don't care. +// 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one. +// 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer +// points to our record, ignore this conflict (e.g. the packet record matches one of our +// TXT records, and that record is marked as dependent on 'our', its SRV record). +// 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record +// are members of the same RRSet, then this is not a conflict. +mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr) + { + const AuthRecord *ourset = our->RRSet ? our->RRSet : our; + + // If not supposed to be unique, not a conflict + if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse); + + // If a dependent record, not a conflict + if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse); + + // If the pktrr matches a member of ourset, not a conflict + if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse); + + // Okay, this is a conflict + return(mDNStrue); + } + +// NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change +// the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, + DNSQuestion *q, AuthRecord *our) + { + int i; + const mDNSu8 *ptr = LocateAuthorities(query, end); + mDNSBool FoundUpdate = mDNSfalse; + + for (i = 0; i < query->h.numAuthorities; i++) + { + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (!ptr) break; + if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + { + FoundUpdate = mDNStrue; + if (PacketRRConflict(m, our, &m->rec.r)) + { + int result = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass; + if (!result) result = (int)our->resrec.rrtype - (int)m->rec.r.resrec.rrtype; + if (!result) result = CompareRData(our, &m->rec.r); + if (result > 0) + debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); + else if (result < 0) + { + debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); + mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); + goto exit; + } + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + if (!FoundUpdate) + debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype)); +exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + +mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr) + { + mDNSu32 slot = HashSlot(pktrr->name); + CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr); + CacheRecord *rr; + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break; + return(rr); + } + +// ProcessQuery examines a received query to see if we have any answers to give +mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, + const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, + mDNSBool QueryWasLocalUnicast, DNSMessage *const response) + { + mDNSBool FromLocalSubnet = AddressIsLocalSubnet(m, InterfaceID, srcaddr); + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated + CacheRecord **eap = &ExpectedAnswers; + DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet + DNSQuestion **dqp = &DupQuestions; + mDNSs32 delayresponse = 0; + mDNSBool SendLegacyResponse = mDNSfalse; + const mDNSu8 *ptr = query->data; + mDNSu8 *responseptr = mDNSNULL; + AuthRecord *rr; + int i; + + // *** + // *** 1. Parse Question Section and mark potential answers + // *** + for (i=0; i<query->h.numQuestions; i++) // For each question... + { + mDNSBool QuestionNeedsMulticastResponse; + int NumAnswersForThisQuestion = 0; + DNSQuestion pktq, *q; + ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... + if (!ptr) goto exit; + + // The only queries that *need* a multicast response are: + // * Queries sent via multicast + // * from port 5353 + // * that don't have the kDNSQClass_UnicastResponse bit set + // These queries need multicast responses because other clients will: + // * suppress their own identical questions when they see these questions, and + // * expire their cache records if they don't see the expected responses + // For other queries, we may still choose to send the occasional multicast response anyway, + // to keep our neighbours caches warm, and for ongoing conflict detection. + QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse); + // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later + pktq.qclass &= ~kDNSQClass_UnicastResponse; + + // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe + // can result in user callbacks which may change the record list and/or question list. + // Also note: we just mark potential answer records here, without trying to build the + // "ResponseRecords" list, because we don't want to risk user callbacks deleting records + // from that list while we're in the middle of trying to build it. + if (m->CurrentRecord) LogMsg("ProcessQuery ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) + { + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + ResolveSimultaneousProbe(m, query, end, &pktq, rr); + else if (ResourceRecordIsValidAnswer(rr)) + { + NumAnswersForThisQuestion++; + // Notes: + // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast) + // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead) + // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later) + // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set, + // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link) + // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source) + if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery)) + { + // We only mark this question for sending if it is at least one second since the last time we multicast it + // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it. + // This is to guard against the case where someone blasts us with queries as fast as they can. + if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 || + (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) + rr->NR_AnswerTo = (mDNSu8*)~0; + } + else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; + } + } + } + + // If we couldn't answer this question, someone else might be able to, + // so use random delay on response to reduce collisions + if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms + + // We only do the following accelerated cache expiration processing and duplicate question suppression processing + // for multicast queries with multicast responses. + // For any query generating a unicast response we don't do this because we can't assume we will see the response + if (QuestionNeedsMulticastResponse) + { + const mDNSu32 slot = HashSlot(&pktq.qname); + CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname); + CacheRecord *rr; + + // Make a list indicating which of our own cache records we expect to see updated as a result of this query + // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && rr->resrec.rdlength <= SmallRecordLimit) + if (!rr->NextInKAList && eap != &rr->NextInKAList) + { + *eap = rr; + eap = &rr->NextInKAList; + if (rr->MPUnansweredQ == 0 || m->timenow - rr->MPLastUnansweredQT >= mDNSPlatformOneSecond) + { + // Although MPUnansweredQ is only really used for multi-packet query processing, + // we increment it for both single-packet and multi-packet queries, so that it stays in sync + // with the MPUnansweredKA value, which by necessity is incremented for both query types. + rr->MPUnansweredQ++; + rr->MPLastUnansweredQT = m->timenow; + rr->MPExpectingKA = mDNStrue; + } + } + + // Check if this question is the same as any of mine. + // We only do this for non-truncated queries. Right now it would be too complicated to try + // to keep track of duplicate suppression state between multiple packets, especially when we + // can't guarantee to receive all of the Known Answer packets that go with a particular query. + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) + for (q = m->Questions; q; q=q->next) + if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) + if (!q->InterfaceID || q->InterfaceID == InterfaceID) + if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) + if (q->qtype == pktq.qtype && + q->qclass == pktq.qclass && + q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) + { *dqp = q; dqp = &q->NextInDQList; } + } + } + + // *** + // *** 2. Now we can safely build the list of marked answers + // *** + for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers + if (rr->NR_AnswerTo) // If we marked the record... + AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list + + // *** + // *** 3. Add additional records + // *** + AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); + + // *** + // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list + // *** + for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section... + { + // Get the record... + AuthRecord *rr; + CacheRecord *ourcacherr; + ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); + if (!ptr) goto exit; + + // See if this Known-Answer suppresses any of our currently planned answers + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) + { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } + + // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) + for (rr=m->ResourceRecords; rr; rr=rr->next) + { + // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression + if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr)) + { + if (srcaddr->type == mDNSAddrType_IPv4) + { + if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; + } + else if (srcaddr->type == mDNSAddrType_IPv6) + { + if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; + } + if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) + { + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); +#endif + } + } + } + + // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, + // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list). + ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); + if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) + { + ourcacherr->MPUnansweredKA++; + ourcacherr->MPExpectingKA = mDNSfalse; + } + + // Having built our ExpectedAnswers list from the questions in this packet, we can definitively + // remove from our ExpectedAnswers list any records that are suppressed in the very same packet. + // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them. + eap = &ExpectedAnswers; + while (*eap) + { + CacheRecord *rr = *eap; + if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) + { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; } + else eap = &rr->NextInKAList; + } + + // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. + if (!ourcacherr) + { + dqp = &DupQuestions; + while (*dqp) + { + DNSQuestion *q = *dqp; + if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } + else dqp = &q->NextInDQList; + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + // *** + // *** 5. Cancel any additionals that were added because of now-deleted records + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo)) + { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } + + // *** + // *** 6. Mark the send flags on the records we plan to send + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + { + if (rr->NR_AnswerTo) + { + mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response + mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response) + + // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc. + if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0) + { + SendMulticastResponse = mDNStrue; + // If this record was marked for modern (delayed) unicast response, then mark it as promoted to + // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below). + // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value. + if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0; + } + + // If the client insists on a multicast response, then we'd better send one + if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; + else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse = mDNStrue; + else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue; + + if (SendMulticastResponse || SendUnicastResponse) + { +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + rr->ImmedAnswerMarkTime = m->timenow; +#endif + m->NextScheduledResponse = m->timenow; + // If we're already planning to send this on another interface, just send it on all interfaces + if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID) + rr->ImmedAnswer = mDNSInterfaceMark; + else + { + rr->ImmedAnswer = InterfaceID; // Record interface to send it on + if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue; + if (srcaddr->type == mDNSAddrType_IPv4) + { + if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4; + else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr; + } + else if (srcaddr->type == mDNSAddrType_IPv6) + { + if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6; + else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr; + } + } + } + // If TC flag is set, it means we should expect that additional known answers may be coming in another packet, + // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11) + // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses + // else, for a simple unique record reply, we can reply immediately; no need for delay + if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms + else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms + } + else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0) + { + // Since additional records are an optimization anyway, we only ever send them on one interface at a time + // If two clients on different interfaces do queries that invoke the same optional additional answer, + // then the earlier client is out of luck + rr->ImmedAdditional = InterfaceID; + // No need to set m->NextScheduledResponse here + // We'll send these additional records when we send them, or not, as the case may be + } + } + + // *** + // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer + // *** + if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50)) + { +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + mDNSs32 oldss = m->SuppressSending; + if (oldss && delayresponse) + LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50); +#endif + // Pick a random delay: + // We start with the base delay chosen above (typically either 1 second or 20 seconds), + // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds). + // This is an integer value, with resolution determined by the platform clock rate. + // We then divide that by 50 to get the delay value in ticks. We defer the division until last + // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second). + // The +49 before dividing is to ensure we round up, not down, to ensure that even + // on platforms where the native clock rate is less than fifty ticks per second, + // we still guarantee that the final calculated delay is at least one platform tick. + // We want to make sure we don't ever allow the delay to be zero ticks, + // because if that happens we'll fail the Bonjour Conformance Test. + // Our final computed delay is 20-120ms for normal delayed replies, + // or 400-500ms in the case of multi-packet known-answer lists. + m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50; + if (m->SuppressSending == 0) m->SuppressSending = 1; +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + if (oldss && delayresponse) + LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow); +#endif + } + + // *** + // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too + // *** + if (SendLegacyResponse) + responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); + +exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + + // *** + // *** 9. Finally, clear our link chains ready for use next time + // *** + while (ResponseRecords) + { + rr = ResponseRecords; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + } + + while (ExpectedAnswers) + { + CacheRecord *rr; + rr = ExpectedAnswers; + ExpectedAnswers = rr->NextInKAList; + rr->NextInKAList = mDNSNULL; + + // For non-truncated queries, we can definitively say that we should expect + // to be seeing a response for any records still left in the ExpectedAnswers list + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) + if (rr->UnansweredQueries == 0 || m->timenow - rr->LastUnansweredTime >= mDNSPlatformOneSecond) + { + rr->UnansweredQueries++; + rr->LastUnansweredTime = m->timenow; + if (rr->UnansweredQueries > 1) + debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); + SetNextCacheCheckTime(m, rr); + } + + // If we've seen multiple unanswered queries for this record, + // then mark it to expire in five seconds if we don't get a response by then. + if (rr->UnansweredQueries >= MaxUnansweredQueries) + { + // Only show debugging message if this record was not about to expire anyway + if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) + debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); + } + // Make a guess, based on the multi-packet query / known answer counts, whether we think we + // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for + // possible packet loss of up to 20% of the additional KA packets.) + else if (rr->MPUnansweredQ * 4 > rr->MPUnansweredKA * 5 + 8) + { + // We want to do this conservatively. + // If there are so many machines on the network that they have to use multi-packet known-answer lists, + // then we don't want them to all hit the network simultaneously with their final expiration queries. + // By setting the record to expire in four minutes, we achieve two things: + // (a) the 90-95% final expiration queries will be less bunched together + // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own + mDNSu32 remain = (mDNSu32)(RRExpireTime(rr) - m->timenow) / 4; + if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond) + remain = 240 * (mDNSu32)mDNSPlatformOneSecond; + + // Only show debugging message if this record was not about to expire anyway + if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) + debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); + + if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) + rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query + rr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; + + if (remain < kDefaultReconfirmTimeForNoAnswer) + remain = kDefaultReconfirmTimeForNoAnswer; + mDNS_Reconfirm_internal(m, rr, remain); + } + } + + while (DupQuestions) + { + int i; + DNSQuestion *q = DupQuestions; + DupQuestions = q->NextInDQList; + q->NextInDQList = mDNSNULL; + i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); + debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, + srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); + } + + return(responseptr); + } + +mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) + { + mDNSu8 *responseend = mDNSNULL; + mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr); + + if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr)) + { + LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); + return; + } + + verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); + + responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, + (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg); + + if (responseend) // If responseend is non-null, that means we built a unicast response packet + { + debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld", + m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", + m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", + srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type); + mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL); + } + } + +// NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change +// the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, + const DNSMessage *const response, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) + { + int i; + + // We ignore questions (if any) in a DNS response packet + const mDNSu8 *ptr = LocateAnswers(response, end); + + // "(CacheRecord*)1" is a special (non-zero) end-of-list marker + // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList + // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling. + CacheRecord *CacheFlushRecords = (CacheRecord*)1; + CacheRecord **cfp = &CacheFlushRecords; + + // All records in a DNS response packet are treated as equally valid statements of truth. If we want + // to guard against spoof responses, then the only credible protection against that is cryptographic + // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record + int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals; + + (void)srcaddr; // Currently used only for display in debugging message + (void)srcport; + (void)dstport; + + verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with " + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + srcaddr, dstaddr, InterfaceID, + response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,", + response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", + response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", + response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); + + // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us + if (!mDNSAddrIsDNSMulticast(dstaddr)) + { + if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2)) + return; + // For now we don't put standard wide-area unicast responses in our main cache + // (Later we should fix this and cache all known results in a unified manner.) + if (response->h.id.NotAnInteger != 0 || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger) + return; + } + + for (i = 0; i < totalrecords && ptr && ptr < end; i++) + { + const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd); + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec); + if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting + + // 1. Check that this packet resource record does not conflict with any of ours + if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match... + { + // ... check to see if type and rdata are identical + if (m->rec.r.resrec.rrtype == rr->resrec.rrtype && SameRData(&m->rec.r.resrec, &rr->resrec)) + { + // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us + if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) + { + // If we were planning to send on this -- and only this -- interface, then we don't need to any more + if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; } + } + else + { + if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; } + else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } + } + } + // else, the packet RR has different type or different rdata -- check to see if this is a conflict + else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r)) + { + debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr-> resrec.rdatahash, ARDisplayString(m, rr)); + debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + + // If this record is marked DependentOn another record for conflict detection purposes, + // then *that* record has to be bumped back to probing state to resolve the conflict + while (rr->DependentOn) rr = rr->DependentOn; + + // If we've just whacked this record's ProbeCount, don't need to do it again + if (rr->ProbeCount <= DefaultProbeCountForTypeUnique) + { + // If we'd previously verified this record, put it back to probing state and try again + if (rr->resrec.RecordType == kDNSRecordTypeVerified) + { + debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique); + InitializeLastAPTime(m, rr); + RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate + } + // If we're probing for this record, we just failed + else if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + // We assumed this record must be unique, but we were wrong. + // (e.g. There are two mDNSResponders on the same machine giving + // different answers for the reverse mapping record.) + // This is simply a misconfiguration, and we don't try to recover from it. + else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) + { + debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + else + debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)", + rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + } + } + // Else, matching signature, different type or rdata, but not a considered a conflict. + // If the packet record has the cache-flush bit set, then we check to see if we + // have any record(s) of the same type that we should re-assert to rescue them + // (see note about "multi-homing and bridged networks" at the end of this function). + else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype) + if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) + { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } + } + } + + // 2. See if we want to add this packet resource record to our cache + if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in + { + const mDNSu32 slot = HashSlot(m->rec.r.resrec.name); + CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); + CacheRecord *rr; + // 2a. Check if this packet resource record is already in our cache + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + // If we found this exact resource record, refresh its TTL + if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) + { + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) + verbosedebugf("Found record size %5d interface %p already in cache: %s", + m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); + rr->TimeRcvd = m->timenow; + + if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list + if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList) + { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } + + // If this packet record is marked unique, and our previous cached copy was not, then fix it + if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; + rr->resrec.RecordType = m->rec.r.resrec.RecordType; + } + } + + if (!mDNSPlatformMemSame(m->rec.r.resrec.rdata->u.data, rr->resrec.rdata->u.data, m->rec.r.resrec.rdlength)) + { + // If the rdata of the packet record differs in name capitalization from the record in our cache + // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get + // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. + rr->resrec.rroriginalttl = 0; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTime(m, rr); + // DO NOT break out here -- we want to continue as if we never found it + } + else if (m->rec.r.resrec.rroriginalttl > 0) + { + rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl; + rr->UnansweredQueries = 0; + rr->MPUnansweredQ = 0; + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; + SetNextCacheCheckTime(m, rr); + break; + } + else + { + // If the packet TTL is zero, that means we're deleting this record. + // To give other hosts on the network a chance to protest, we push the deletion + // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. + // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent + // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. + rr->resrec.rroriginalttl = 1; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTime(m, rr); + break; + } + } + } + + // If packet resource record not in our cache, add it now + // (unless it is just a deletion of a record we never had, in which case we don't care) + if (!rr && m->rec.r.resrec.rroriginalttl > 0) + { + // If we don't have a CacheGroup for this name, make one now + if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec); + if (cg) rr = GetCacheRecord(m, cg, m->rec.r.resrec.rdlength); // Make a cache record, being careful not to recycle cg + if (!rr) NoCacheAnswer(m, &m->rec.r); + else + { + RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer + *rr = m->rec.r; // Block copy the CacheRecord object + rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment + rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } + // If this is an oversized record with external storage allocated, copy rdata to external storage + if (rr->resrec.rdata != (RData*)&rr->rdatastorage && !(m->rec.r.resrec.rdlength > InlineCacheRDSize)) + LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c); + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) + mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength); + rr->next = mDNSNULL; // Clear 'next' pointer + *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list + cg->rrcache_tail = &(rr->next); // Advance tail pointer + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) // If marked unique, assume we may have + rr->DelayDelivery = m->timenow + mDNSPlatformOneSecond; // to delay delivery of this 'add' event + else + rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot); + CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us + } + } + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + +exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + + // If we've just received one or more records with their cache flush bits set, + // then scan that cache slot to see if there are any old stale records we need to flush + while (CacheFlushRecords != (CacheRecord*)1) + { + CacheRecord *r1 = CacheFlushRecords, *r2; + const mDNSu32 slot = HashSlot(r1->resrec.name); + CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec); + CacheFlushRecords = CacheFlushRecords->NextInCFList; + r1->NextInCFList = mDNSNULL; + for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next) + if (SameResourceRecordSignature(&r1->resrec, &r2->resrec)) + { + // If record was recently positively received + // (i.e. not counting goodbye packets or cache flush events that set the TTL to 1) + // then we need to ensure the whole RRSet has the same TTL (as required by DNS semantics) + if (r2->resrec.rroriginalttl > 1 && m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond) + { + if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl) + LogMsg("Correcting TTL from %4d to %4d for %s", + r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2)); + r2->resrec.rroriginalttl = r1->resrec.rroriginalttl; + r2->TimeRcvd = m->timenow; + } + else // else, if record is old, mark it to be flushed + { + verbosedebugf("Cache flush %p X %p %s", r1, r2, CRDisplayString(m, r2)); + // We set stale records to expire in one second. + // This gives the owner a chance to rescue it if necessary. + // This is important in the case of multi-homing and bridged networks: + // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be + // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit + // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet + // will promptly delete their cached copies of the (still valid) Ethernet IP address record. + // By delaying the deletion by one second, we give X a change to notice that this bridging has + // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. + // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary + // final expiration queries for this record. + r2->resrec.rroriginalttl = 1; + r2->TimeRcvd = m->timenow; + r2->UnansweredQueries = MaxUnansweredQueries; + } + SetNextCacheCheckTime(m, r2); + } + if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to + { + // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared + r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot); + if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); + } + } + } + +mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) + { + DNSMessage *msg = (DNSMessage *)pkt; + const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; + const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + mDNSu8 QR_OP; + mDNSu8 *ptr = mDNSNULL; + const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + +#ifndef UNICAST_DISABLED + if (srcport.NotAnInteger == NATPMPPort.NotAnInteger) + { + mDNS_Lock(m); + uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + mDNS_Unlock(m); + return; + } +#endif + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; } + QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + // Read the integer parts which are in IETF byte-order (MSB first, LSB second) + ptr = (mDNSu8 *)&msg->h.numQuestions; + msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + + if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; } + + // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address" + // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up + if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } + + mDNS_Lock(m); + m->PktNum++; +#ifndef UNICAST_DISABLED + if (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdateR)) + uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + // Note: mDNSCore also needs to get access to received unicast responses +#endif + if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + else if (QR_OP != UpdateR) + LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)", + msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID); + + // Packet reception often causes a change to the task list: + // 1. Inbound queries can cause us to need to send responses + // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses + // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records + // 4. Response packets that answer questions may cause our client to issue new questions + mDNS_Unlock(m); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - Searcher Functions +#endif + +#define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger) + +mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question) + { + DNSQuestion *q; + // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list. + // This prevents circular references, where two questions are each marked as a duplicate of the other. + // Accordingly, we break out of the loop when we get to 'question', because there's no point searching + // further in the list. + for (q = m->Questions; q && q != question; q=q->next) // Scan our list of questions + if (q->InterfaceID == question->InterfaceID && // for another question with the same InterfaceID, + SameQTarget(q, question) && // and same unicast/multicast target settings + q->qtype == question->qtype && // type, + q->qclass == question->qclass && // class, + q->qnamehash == question->qnamehash && + SameDomainName(&q->qname, &question->qname)) // and name + return(q); + return(mDNSNULL); + } + +// This is called after a question is deleted, in case other identical questions were being +// suppressed as duplicates +mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate + { + q->ThisQInterval = question->ThisQInterval; + q->RequestUnicast = question->RequestUnicast; + q->LastQTime = question->LastQTime; + q->RecentAnswerPkts = 0; + q->DuplicateOf = FindDuplicateQuestion(m, q); + q->LastQTxTime = question->LastQTxTime; + SetNextQueryTime(m,q); + } + } + +#define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \ + ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger)) + +mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) + { + if (question->Target.type && !ValidQuestionTarget(question)) + { + LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)", + question->Target.type, mDNSVal16(question->TargetPort)); + question->Target.type = mDNSAddrType_None; + } + + if (!question->Target.type) // No question->Target specified, so clear TargetPort and TargetQID + { + question->TargetPort = zeroIPPort; + question->TargetQID = zeroID; + } + +#ifndef UNICAST_DISABLED + // If the client has specified 'kDNSServiceFlagsForceMulticast' + // then we do a multicast query on that interface, even for unicast domains. + if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) + question->uDNS_info.id = zeroID; + else return uDNS_StartQuery(m, question); +#else + question->uDNS_info.id = zeroID; +#endif // UNICAST_DISABLED + + //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + + if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated + return(mStatus_NoCache); + else + { + int i; + // Note: It important that new questions are appended at the *end* of the list, not prepended at the start + DNSQuestion **q = &m->Questions; + if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; + while (*q && *q != question) q=&(*q)->next; + + if (*q) + { + LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list", + question->qname.c, DNSTypeName(question->qtype)); + return(mStatus_AlreadyRegistered); + } + + // If this question is referencing a specific interface, verify it exists + if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly) + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == question->InterfaceID) break; + if (!intf) + LogMsg("Note: InterfaceID %p for question %##s not currently found in active interface list", + question->InterfaceID, question->qname.c); + } + + if (!ValidateDomainName(&question->qname)) + { + LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return(mStatus_Invalid); + } + + // Note: In the case where we already have the answer to this question in our cache, that may be all the client + // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would + // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds + // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately. + if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); + + question->next = mDNSNULL; + question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() + question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname)); + question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question + question->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it + question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization + question->LastAnswerPktNum = m->PktNum; + question->RecentAnswerPkts = 0; + question->CurrentAnswers = 0; + question->LargeAnswers = 0; + question->UniqueAnswers = 0; + question->FlappingInterface = mDNSNULL; + question->DuplicateOf = FindDuplicateQuestion(m, question); + question->NextInDQList = mDNSNULL; + for (i=0; i<DupSuppressInfoSize; i++) + question->DupSuppress[i].InterfaceID = mDNSNULL; + // question->InterfaceID must be already set by caller + question->SendQNow = mDNSNULL; + question->SendOnAll = mDNSfalse; + question->LastQTxTime = m->timenow; + + if (!question->DuplicateOf) + verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, + question->LastQTime + question->ThisQInterval - m->timenow, question); + else + verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, + question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf); + + *q = question; + if (question->InterfaceID == mDNSInterface_LocalOnly) + { + if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; + } + else + { + if (!m->NewQuestions) m->NewQuestions = question; + SetNextQueryTime(m,question); + } + + return(mStatus_NoError); + } + } + +mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question) + { + const mDNSu32 slot = HashSlot(&question->qname); + CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname); + CacheRecord *rr; + DNSQuestion **q = &m->Questions; + + if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question); + + if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; + while (*q && *q != question) q=&(*q)->next; + if (*q) *q = (*q)->next; + else + { + if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active + LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", + question->qname.c, DNSTypeName(question->qtype)); + return(mStatus_BadReferenceErr); + } + + // Take care to cut question from list *before* calling UpdateQuestionDuplicates + UpdateQuestionDuplicates(m, question); + // But don't trash ThisQInterval until afterwards. + question->ThisQInterval = -1; + + // If there are any cache records referencing this as their active question, then see if any other + // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL. + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + if (rr->CRActiveQuestion == question) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + break; + verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", + rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), q); + rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null + if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count + } + } + + // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at, + // bump its pointer forward one question. + if (m->CurrentQuestion == question) + { + debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->CurrentQuestion = question->next; + } + + if (m->NewQuestions == question) + { + debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->NewQuestions = question->next; + } + + if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next; + + // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions + question->next = mDNSNULL; + return(mStatus_NoError); + } + +mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question) + { + mStatus status; + mDNS_Lock(m); + status = mDNS_StartQuery_internal(m, question); + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) + { + mStatus status; + mDNS_Lock(m); + status = mDNS_StopQuery_internal(m, question); + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr) + { + mStatus status; + mDNS_Lock(m); + status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr) + { + mStatus status = mStatus_BadReferenceErr; + CacheRecord *cr; + mDNS_Lock(m); + cr = FindIdenticalRecordInCache(m, rr); + if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, + const domainname *const srv, const domainname *const domain, + const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context) + { + question->InterfaceID = InterfaceID; + question->Target = zeroAddr; + question->qtype = kDNSType_PTR; + question->qclass = kDNSClass_IN; + question->LongLived = mDNSfalse; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = ForceMCast; + question->QuestionCallback = Callback; + question->QuestionContext = Context; + if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); + +#ifndef UNICAST_DISABLED + if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) + { + question->LongLived = mDNSfalse; + question->uDNS_info.id = zeroID; + return(mDNS_StartQuery(m, question)); + } + else + { + mStatus status; + // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not + mDNS_Lock(m); + question->LongLived = mDNStrue; + status = uDNS_StartQuery(m, question); + mDNS_Unlock(m); + return(status); + } +#else + return(mDNS_StartQuery(m, question)); +#endif // UNICAST_DISABLED + } + +mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m) + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); + return(mDNSfalse); + } + +mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger); + if (!AddRecord) return; + if (answer->rrtype != kDNSType_SRV) return; + + query->info->port = answer->rdata->u.srv.port; + + // If this is our first answer, then set the GotSRV flag and start the address query + if (!query->GotSRV) + { + query->GotSRV = mDNStrue; + query->qAv4.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); + query->qAv6.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); + mDNS_StartQuery(m, &query->qAv4); + // Only do the AAAA query if this machine actually has IPv6 active + if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); + } + // If this is not our first answer, only re-issue the address query if the target host name has changed + else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || + !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) + { + mDNS_StopQuery(m, &query->qAv4); + if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6); + if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) + { + // If we get here, it means: + // 1. This is not our first SRV answer + // 2. The interface ID is different, but the target host and port are the same + // This implies that we're seeing the exact same SRV record on more than one interface, so we should + // make our address queries at least as broad as the original SRV query so that we catch all the answers. + query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface + query->qAv6.InterfaceID = query->qSRV.InterfaceID; + } + else + { + query->qAv4.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); + query->qAv6.InterfaceID = answer->InterfaceID; + AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); + } + debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c); + mDNS_StartQuery(m, &query->qAv4); + // Only do the AAAA query if this machine actually has IPv6 active + if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); + } + else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) + { + if (++query->Answers >= 100) + debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", + query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, + mDNSVal16(answer->rdata->u.srv.port)); + query->ServiceInfoQueryCallback(m, query); + } + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. + } + +mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + if (!AddRecord) return; + if (answer->rrtype != kDNSType_TXT) return; + if (answer->rdlength > sizeof(query->info->TXTinfo)) return; + + query->GotTXT = mDNStrue; + query->info->TXTlen = answer->rdlength; + query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero + mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength); + + verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); + + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. + if (query->ServiceInfoQueryCallback && query->GotADD) + { + if (++query->Answers >= 100) + debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", + query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); + query->ServiceInfoQueryCallback(m, query); + } + } + +mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer)); + if (!AddRecord) return; + + if (answer->rrtype == kDNSType_A) + { + query->info->ip.type = mDNSAddrType_IPv4; + query->info->ip.ip.v4 = answer->rdata->u.ipv4; + } + else if (answer->rrtype == kDNSType_AAAA) + { + query->info->ip.type = mDNSAddrType_IPv6; + query->info->ip.ip.v6 = answer->rdata->u.ipv6; + } + else + { + debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype)); + return; + } + + query->GotADD = mDNStrue; + query->info->InterfaceID = answer->InterfaceID; + + verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); + + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. + if (query->ServiceInfoQueryCallback && query->GotTXT) + { + if (++query->Answers >= 100) + debugf(answer->rrtype == kDNSType_A ? + "**** WARNING **** have given %lu answers for %##s (A) %.4a" : + "**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", + query->Answers, query->qSRV.qname.c, &answer->rdata->u.data); + query->ServiceInfoQueryCallback(m, query); + } + } + +// On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure +// If the query is not interface-specific, then InterfaceID may be zero +// Each time the Callback is invoked, the remainder of the fields will have been filled in +// In addition, InterfaceID will be updated to give the interface identifier corresponding to that response +mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, + ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) + { + mStatus status; + mDNS_Lock(m); + + query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qSRV.InterfaceID = info->InterfaceID; + query->qSRV.Target = zeroAddr; + AssignDomainName(&query->qSRV.qname, &info->name); + query->qSRV.qtype = kDNSType_SRV; + query->qSRV.qclass = kDNSClass_IN; + query->qSRV.LongLived = mDNSfalse; + query->qSRV.ExpectUnique = mDNStrue; + query->qSRV.ForceMCast = mDNSfalse; + query->qSRV.QuestionCallback = FoundServiceInfoSRV; + query->qSRV.QuestionContext = query; + + query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qTXT.InterfaceID = info->InterfaceID; + query->qTXT.Target = zeroAddr; + AssignDomainName(&query->qTXT.qname, &info->name); + query->qTXT.qtype = kDNSType_TXT; + query->qTXT.qclass = kDNSClass_IN; + query->qTXT.LongLived = mDNSfalse; + query->qTXT.ExpectUnique = mDNStrue; + query->qTXT.ForceMCast = mDNSfalse; + query->qTXT.QuestionCallback = FoundServiceInfoTXT; + query->qTXT.QuestionContext = query; + + query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qAv4.InterfaceID = info->InterfaceID; + query->qAv4.Target = zeroAddr; + query->qAv4.qname.c[0] = 0; + query->qAv4.qtype = kDNSType_A; + query->qAv4.qclass = kDNSClass_IN; + query->qAv4.LongLived = mDNSfalse; + query->qAv4.ExpectUnique = mDNStrue; + query->qAv4.ForceMCast = mDNSfalse; + query->qAv4.QuestionCallback = FoundServiceInfo; + query->qAv4.QuestionContext = query; + + query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question + query->qAv6.InterfaceID = info->InterfaceID; + query->qAv6.Target = zeroAddr; + query->qAv6.qname.c[0] = 0; + query->qAv6.qtype = kDNSType_AAAA; + query->qAv6.qclass = kDNSClass_IN; + query->qAv6.LongLived = mDNSfalse; + query->qAv6.ExpectUnique = mDNStrue; + query->qAv6.ForceMCast = mDNSfalse; + query->qAv6.QuestionCallback = FoundServiceInfo; + query->qAv6.QuestionContext = query; + + query->GotSRV = mDNSfalse; + query->GotTXT = mDNSfalse; + query->GotADD = mDNSfalse; + query->Answers = 0; + + query->info = info; + query->ServiceInfoQueryCallback = Callback; + query->ServiceInfoQueryContext = Context; + +// info->name = Must already be set up by client +// info->interface = Must already be set up by client + info->ip = zeroAddr; + info->port = zeroIPPort; + info->TXTlen = 0; + + // We use mDNS_StartQuery_internal here because we're already holding the lock + status = mDNS_StartQuery_internal(m, &query->qSRV); + if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT); + if (status != mStatus_NoError) mDNS_StopResolveService(m, query); + + mDNS_Unlock(m); + return(status); + } + +mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q) + { + mDNS_Lock(m); + // We use mDNS_StopQuery_internal here because we're already holding the lock + if (q->qSRV.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qSRV, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qSRV); + if (q->qTXT.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qTXT, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qTXT); + if (q->qAv4.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv4, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv4); + if (q->qAv6.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv6, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv6); + mDNS_Unlock(m); + } + +mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) + { + question->InterfaceID = InterfaceID; + question->Target = zeroAddr; + question->qtype = kDNSType_PTR; + question->qclass = kDNSClass_IN; + question->LongLived = mDNSfalse; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = mDNSfalse; + question->QuestionCallback = Callback; + question->QuestionContext = Context; + if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); + if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); + if (!dom) dom = &localdomain; + if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr); + return(mDNS_StartQuery(m, question)); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Responder Functions +#endif + +mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) + { + mStatus status; + mDNS_Lock(m); + status = mDNS_Register_internal(m, rr); + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) + { +#ifndef UNICAST_DISABLED + mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name)); +#else + mDNSBool unicast = mDNSfalse; +#endif + + if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) + { + LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); + return(mStatus_Invalid); + } + + mDNS_Lock(m); + + // If TTL is unspecified, leave TTL unchanged + if (newttl == 0) newttl = rr->resrec.rroriginalttl; + + // If we already have an update queued up which has not gone through yet, + // give the client a chance to free that memory + if (!unicast && rr->NewRData) + { + RData *n = rr->NewRData; + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary + } + + rr->NewRData = newrdata; + rr->newrdlength = newrdlength; + rr->UpdateCallback = Callback; + + if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); } + + if (rr->resrec.rroriginalttl == newttl && + rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)) + CompleteRDataUpdate(m, rr); + else + { + domainlabel name; + domainname type, domain; + DeconstructServiceName(rr->resrec.name, &name, &type, &domain); + rr->AnnounceCount = InitialAnnounceCount; + // iChat often does suprious record updates where no data has changed. For the _presence service type, using + // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful + // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data + // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us. + // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement. + if (SameDomainLabel(type.c, (mDNSu8*)"\x6_ichat")) rr->AnnounceCount = 1; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m, rr); + while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); + if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; + if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval); + if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); + if (rr->UpdateCredits <= 5) + { + mDNSu32 delay = 6 - rr->UpdateCredits; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum + if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + (mDNSs32)delay * mDNSPlatformOneSecond); + rr->ThisAPInterval *= 4; + rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval; + LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", + rr->resrec.name->c, delay, delay > 1 ? "s" : ""); + } + rr->resrec.rroriginalttl = newttl; + } + + mDNS_Unlock(m); + return(mStatus_NoError); + } + +// NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change +// the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) + { + mStatus status; + mDNS_Lock(m); + status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + mDNS_Unlock(m); + return(status); + } + +mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); + +mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) break; + return(intf); + } + +mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) + { + char buffer[256]; + NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); + if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary + + // Send dynamic update for non-linklocal IPv4 Addresses + mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, mDNS_HostNameCallback, set); + mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); + +#if ANSWER_REMOTE_HOSTNAME_QUERIES + set->RR_A .AllowRemoteQuery = mDNStrue; + set->RR_PTR .AllowRemoteQuery = mDNStrue; + set->RR_HINFO.AllowRemoteQuery = mDNStrue; +#endif + // 1. Set up Address record to map from host name ("foo.local.") to IP address + // 2. Set up reverse-lookup PTR record to map from our address back to our host name + AssignDomainName(set->RR_A.resrec.name, &m->MulticastHostname); + if (set->ip.type == mDNSAddrType_IPv4) + { + set->RR_A.resrec.rrtype = kDNSType_A; + set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; + // Note: This is reverse order compared to a normal dotted-decimal IP address + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", + set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); + } + else if (set->ip.type == mDNSAddrType_IPv6) + { + int i; + set->RR_A.resrec.rrtype = kDNSType_AAAA; + set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6; + for (i = 0; i < 16; i++) + { + static const char hexValues[] = "0123456789ABCDEF"; + buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F]; + buffer[i * 4 + 1] = '.'; + buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4]; + buffer[i * 4 + 3] = '.'; + } + mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); + } + + MakeDomainNameFromDNSNameString(set->RR_PTR.resrec.name, buffer); + set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name + set->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server + + set->RR_A.RRSet = &primary->RR_A; // May refer to self + + mDNS_Register_internal(m, &set->RR_A); + mDNS_Register_internal(m, &set->RR_PTR); + + if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254) + { + mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data; + AssignDomainName(set->RR_HINFO.resrec.name, &m->MulticastHostname); + set->RR_HINFO.DependentOn = &set->RR_A; + mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]); + p += 1 + (int)p[0]; + mDNSPlatformMemCopy(&m->HISoftware, p, 1 + (mDNSu32)m->HISoftware.c[0]); + mDNS_Register_internal(m, &set->RR_HINFO); + } + else + { + debugf("Not creating HINFO record: platform support layer provided no information"); + set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered; + } + } + +mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) + { + NetworkInterfaceInfo *intf; + + // If we still have address records referring to this one, update them + NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); + AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->RR_A.RRSet == &set->RR_A) + intf->RR_A.RRSet = A; + + // Unregister these records. + // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform + // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. + // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. + // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). + if (set->RR_A. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); + if (set->RR_PTR. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); + if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); + } + +mDNSexport void mDNS_SetFQDN(mDNS *const m) + { + domainname newmname; + NetworkInterfaceInfo *intf; + AuthRecord *rr; + newmname.c[0] = 0; + + if (!AppendDomainLabel(&newmname, &m->hostlabel)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } + if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } + if (SameDomainName(&m->MulticastHostname, &newmname)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; } + + mDNS_Lock(m); + AssignDomainName(&m->MulticastHostname, &newmname); + + // 1. Stop advertising our address records on all interfaces + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) DeadvertiseInterface(m, intf); + + // 2. Start advertising our address records using the new name + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) AdvertiseInterface(m, intf); + + // 3. Make sure that any SRV records (and the like) that reference our + // host name in their rdata get updated to reference this new host name + for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); + for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); + + mDNS_Unlock(m); + } + +mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)rr; // Unused parameter + + #if MDNS_DEBUGMSGS + { + char *msg = "Unknown result"; + if (result == mStatus_NoError) msg = "Name registered"; + else if (result == mStatus_NameConflict) msg = "Name conflict"; + debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); + } + #endif + + if (result == mStatus_NoError) + { + // Notify the client that the host name is successfully registered + if (m->MainCallback) + { + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + m->MainCallback(m, result); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } + } + else if (result == mStatus_NameConflict) + { + domainlabel oldlabel = m->hostlabel; + + // 1. First give the client callback a chance to pick a new name + if (m->MainCallback) + { + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + m->MainCallback(m, mStatus_NameConflict); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } + + // 2. If the client callback didn't do it, add (or increment) an index ourselves + if (SameDomainLabel(m->hostlabel.c, oldlabel.c)) + IncrementLabelSuffix(&m->hostlabel, mDNSfalse); + + // 3. Generate the FQDNs from the hostlabel, + // and make sure all SRV records, etc., are updated to reference our new hostname + mDNS_SetFQDN(m); + LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c); + } + else if (result == mStatus_MemFree) + { + // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by + // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface + debugf("mDNS_HostNameCallback: MemFree (ignored)"); + } + else + LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name->c); + } + +mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) + { + NetworkInterfaceInfo *intf; + active->IPv4Available = mDNSfalse; + active->IPv6Available = mDNSfalse; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == active->InterfaceID) + { + if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue; + if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue; + } + } + +mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping) + { + mDNSBool FirstOfType = mDNStrue; + NetworkInterfaceInfo **p = &m->HostInterfaces; + + if (!set->InterfaceID) + { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } + + if (!mDNSAddressIsValidNonZero(&set->mask)) + { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); } + + mDNS_Lock(m); + + // Assume this interface will be active now, unless we find a duplicate already in the list + set->InterfaceActive = mDNStrue; + set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); + set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); + + // Scan list to see if this InterfaceID is already represented + while (*p) + { + if (*p == set) + { + LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list"); + mDNS_Unlock(m); + return(mStatus_AlreadyRegistered); + } + + if ((*p)->InterfaceID == set->InterfaceID) + { + // This InterfaceID already represented by a different interface in the list, so mark this instance inactive for now + set->InterfaceActive = mDNSfalse; + if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse; + if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue; + if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue; + } + + p=&(*p)->next; + } + + set->next = mDNSNULL; + *p = set; + + if (set->Advertise) + AdvertiseInterface(m, set); + + LogOperation("mDNS_RegisterInterface: InterfaceID %p %s (%#a) %s", set->InterfaceID, set->ifname, &set->ip, + set->InterfaceActive ? + "not represented in list; marking active and retriggering queries" : + "already represented in list; marking inactive for now"); + + // In early versions of OS X the IPv6 address remains on an interface even when the interface is turned off, + // giving the false impression that there's an active representative of this interface when there really isn't. + // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records, + // even if we believe that we previously had an active representative of this interface. + if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive)) + { + DNSQuestion *q; + AuthRecord *rr; + // If flapping, delay between first and second queries is eight seconds instead of one + mDNSs32 delay = flapping ? mDNSPlatformOneSecond * 5 : 0; + mDNSu8 announce = flapping ? (mDNSu8)1 : InitialAnnounceCount; + + // Use a small amount of randomness: + // In the case of a network administrator turning on an Ethernet hub so that all the + // connected machines establish link at exactly the same time, we don't want them all + // to go and hit the network with identical queries at exactly the same moment. + if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); + + if (flapping) + { + LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect", set->ifname, &set->ip); + if (!m->SuppressProbes || + m->SuppressProbes - (m->timenow + delay) < 0) + m->SuppressProbes = (m->timenow + delay); + } + + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, + { // then reactivate this question + mDNSs32 initial = (flapping && q->FlappingInterface != set->InterfaceID) ? InitialQuestionInterval * 8 : InitialQuestionInterval; + mDNSs32 qdelay = (flapping && q->FlappingInterface != set->InterfaceID) ? mDNSPlatformOneSecond * 5 : 0; + if (flapping && q->FlappingInterface == set->InterfaceID) + LogOperation("No cache records for %##s (%s) expired; no need for immediate question", q->qname.c, DNSTypeName(q->qtype)); + + if (!q->ThisQInterval || q->ThisQInterval > initial) + { + q->ThisQInterval = initial; + q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it + } + if (q->LastQTime - (m->timenow - q->ThisQInterval + qdelay) > 0) + q->LastQTime = (m->timenow - q->ThisQInterval + qdelay); + q->RecentAnswerPkts = 0; + SetNextQueryTime(m,q); + } + + // For all our non-specific authoritative resource records (and any dormant records specific to this interface) + // we now need them to re-probe if necessary, and then re-announce. + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) + { + if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + if (rr->AnnounceCount < announce) rr->AnnounceCount = announce; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m, rr); + } + } + + mDNS_Unlock(m); + return(mStatus_NoError); + } + +// NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change +// the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping) + { + NetworkInterfaceInfo **p = &m->HostInterfaces; + + mDNSBool revalidate = mDNSfalse; + // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every + // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it + // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion. + if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue; + + mDNS_Lock(m); + + // Find this record in our list + while (*p && *p != set) p=&(*p)->next; + if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; } + + // Unlink this record from our list + *p = (*p)->next; + set->next = mDNSNULL; + + if (!set->InterfaceActive) + { + // If this interface not the active member of its set, update the v4/v6Available flags for the active member + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) + UpdateInterfaceProtocols(m, intf); + } + else + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == set->InterfaceID) + break; + if (intf) + { + LogOperation("mDNS_DeregisterInterface: Another representative of InterfaceID %p %s (%#a) exists;" + " making it active", set->InterfaceID, set->ifname, &set->ip); + intf->InterfaceActive = mDNStrue; + UpdateInterfaceProtocols(m, intf); + + // See if another representative *of the same type* exists. If not, we mave have gone from + // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid. + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type) + break; + if (!intf) revalidate = mDNStrue; + } + else + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + DNSQuestion *q; + LogOperation("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;" + " marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip); + + if (flapping) + LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect", + set->ifname, &set->ip); + + // 1. Deactivate any questions specific to this interface, and tag appropriate questions + // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them + for (q = m->Questions; q; q=q->next) + { + if (q->InterfaceID == set->InterfaceID) q->ThisQInterval = 0; + if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) + q->FlappingInterface = set->InterfaceID; + } + + // 2. Flush any cache records received on this interface + revalidate = mDNSfalse; // Don't revalidate if we're flushing the records + FORALL_CACHERECORDS(slot, cg, rr) + if (rr->resrec.InterfaceID == set->InterfaceID) + { + // If this interface is deemed flapping, + // postpone deleting the cache records in case the interface comes back again + if (!flapping) PurgeCacheResourceRecord(m, rr); + else mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); + } + } + } + + // If we were advertising on this interface, deregister those address and reverse-lookup records now + if (set->Advertise) DeadvertiseInterface(m, set); + + // If we have any cache records received on this interface that went away, then re-verify them. + // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, + // giving the false impression that there's an active representative of this interface when there really isn't. + // Don't need to do this when shutting down, because *all* interfaces are about to go away + if (revalidate && !m->mDNS_shutdown) + { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + m->NextCacheCheck = m->timenow; + FORALL_CACHERECORDS(slot, cg, rr) + if (rr->resrec.InterfaceID == set->InterfaceID) + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); + } + + mDNS_Unlock(m); + } + +mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + (void)m; // Unused parameter + + #if MDNS_DEBUGMSGS + { + char *msg = "Unknown result"; + if (result == mStatus_NoError) msg = "Name Registered"; + else if (result == mStatus_NameConflict) msg = "Name Conflict"; + else if (result == mStatus_MemFree) msg = "Memory Free"; + debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result); + } + #endif + + // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing) + if (result == mStatus_NoError && rr != &sr->RR_SRV) return; + + // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that + if (result == mStatus_NameConflict) + { + sr->Conflict = mDNStrue; // Record that this service set had a conflict + mDNS_DeregisterService(m, sr); // Unlink the records from our list + return; + } + + if (result == mStatus_MemFree) + { + // If the PTR record or any of the subtype PTR records are still in the process of deregistering, + // don't pass on the NameConflict/MemFree message until every record is finished cleaning up. + mDNSu32 i; + if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return; + for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return; + + // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, + // then we can now report the NameConflict to the client + if (sr->Conflict) result = mStatus_NameConflict; + } + + // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback + // function is allowed to do anything, including deregistering this service and freeing its memory. + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); + } + +mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); + } + +// Note: +// Name is first label of domain name (any dots in the name are actual dots, not label separators) +// Type is service type (e.g. "_ipp._tcp.") +// Domain is fully qualified domain name (i.e. ending with a null label) +// We always register a TXT, even if it is empty (so that clients are not +// left waiting forever looking for a nonexistent record.) +// If the host parameter is mDNSNULL or the root domain (ASCII NUL), +// then the default host name (m->MulticastHostname) is automatically used +mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context) + { + mStatus err; + mDNSu32 i; + + sr->ServiceCallback = Callback; + sr->ServiceContext = Context; + sr->Extras = mDNSNULL; + sr->NumSubTypes = NumSubTypes; + sr->SubTypes = SubTypes; + sr->Conflict = mDNSfalse; + if (host && host->c[0]) sr->Host = *host; + else sr->Host.c[0] = 0; + + // If port number is zero, that means the client is really trying to do a RegisterNoSuchService + if (!port.NotAnInteger) + return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr)); + + // Initialize the AuthRecord objects to sane values + mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, ServiceCallback, sr); + + // If the client is registering an oversized TXT record, + // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it + if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) + sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; + + // Set up the record names + // For now we only create an advisory record for the main type, not for subtypes + // We need to gain some operational experience before we decide if there's a need to create them for subtypes too + if (ConstructServiceName(sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL) + return(mStatus_BadParamErr); + if (ConstructServiceName(sr->RR_PTR.resrec.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + if (ConstructServiceName(sr->RR_SRV.resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name); + + // 1. Set up the ADV record rdata to advertise our service type + AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name); + + // 2. Set up the PTR record rdata to point to our service name + // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too + AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name); + sr->RR_PTR.Additional1 = &sr->RR_SRV; + sr->RR_PTR.Additional2 = &sr->RR_TXT; + + // 2a. Set up any subtype PTRs to point to our service name + // If the client is using subtypes, it is the client's responsibility to have + // already set the first label of the record name to the subtype being registered + for (i=0; i<NumSubTypes; i++) + { + domainname st; + AssignDomainName(&st, sr->SubTypes[i].resrec.name); + st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService()) + AppendDomainName(&st, type); + mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); + if (ConstructServiceName(sr->SubTypes[i].resrec.name, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr); + AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, sr->RR_SRV.resrec.name); + sr->SubTypes[i].Additional1 = &sr->RR_SRV; + sr->SubTypes[i].Additional2 = &sr->RR_TXT; + } + + // 3. Set up the SRV record rdata. + sr->RR_SRV.resrec.rdata->u.srv.priority = 0; + sr->RR_SRV.resrec.rdata->u.srv.weight = 0; + sr->RR_SRV.resrec.rdata->u.srv.port = port; + + // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name + if (sr->Host.c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, &sr->Host); + else { sr->RR_SRV.HostTarget = mDNStrue; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; } + + // 4. Set up the TXT record rdata, + // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us + if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0; + else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c) + { + sr->RR_TXT.resrec.rdlength = txtlen; + if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr); + mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.resrec.rdata->u.txt.c, txtlen); + } + sr->RR_TXT.DependentOn = &sr->RR_SRV; + +#ifndef UNICAST_DISABLED + // If the client has specified an explicit InterfaceID, + // then we do a multicast registration on that interface, even for unicast domains. + if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) + { + mStatus status; + mDNS_Lock(m); + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck) + if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; } + status = uDNS_RegisterService(m, sr); + mDNS_Unlock(m); + return(status); + } +#endif + mDNS_Lock(m); + err = mDNS_Register_internal(m, &sr->RR_SRV); + if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); + // We register the RR_PTR last, because we want to be sure that in the event of a forced call to + // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers + // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to + // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to + // make sure we've deregistered all our records and done any other necessary cleanup before that happens. + if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV); + for (i=0; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[i]); + if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR); + + mDNS_Unlock(m); + + if (err) mDNS_DeregisterService(m, sr); + return(err); + } + +mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, + ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl) + { + ExtraResourceRecord **e; + mStatus status; + + extra->next = mDNSNULL; + mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, + extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); + AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name); + +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) + { + mDNS_Lock(m); + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck) + if (extra->r.resrec.rrtype == kDNSType_TXT && extra->r.resrec.rdlength == 0) + { extra->r.resrec.rdlength = 1; extra->r.resrec.rdata->u.txt.c[0] = 0; } + status = uDNS_AddRecordToService(m, sr, extra); + mDNS_Unlock(m); + return status; + } +#endif + + mDNS_Lock(m); + e = &sr->Extras; + while (*e) e = &(*e)->next; + + if (ttl == 0) ttl = kStandardTTL; + + extra->r.DependentOn = &sr->RR_SRV; + + debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name->c); + + status = mDNS_Register_internal(m, &extra->r); + if (status == mStatus_NoError) *e = extra; + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, + mDNSRecordCallback MemFreeCallback, void *Context) + { + ExtraResourceRecord **e; + mStatus status; + + mDNS_Lock(m); + e = &sr->Extras; + while (*e && *e != extra) e = &(*e)->next; + if (!*e) + { + debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c); + status = mStatus_BadReferenceErr; + } + else + { + debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c); + extra->r.RecordCallback = MemFreeCallback; + extra->r.RecordContext = Context; + *e = (*e)->next; +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) + status = uDNS_DeregisterRecord(m, &extra->r); + else +#endif + status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); + } + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname) + { + // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines + // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally. + domainlabel name1, name2; + domainname type, domain; + domainname *host = mDNSNULL; + ExtraResourceRecord *extras = sr->Extras; + mStatus err; + + DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain); + if (!newname) + { + name2 = name1; + IncrementLabelSuffix(&name2, mDNStrue); + newname = &name2; + } + + if (SameDomainName(&domain, &localdomain)) + LogMsg("%##s service renamed from \"%#s\" to \"%#s\"", type.c, name1.c, newname->c); + else LogMsg("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c); + + if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host; + + err = mDNS_RegisterService(m, sr, newname, &type, &domain, + host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, + sr->SubTypes, sr->NumSubTypes, + sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext); + + // mDNS_RegisterService() just reset sr->Extras to NULL. + // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run + // through the old list of extra records, and re-add them to our freshly created service registration + while (!err && extras) + { + ExtraResourceRecord *e = extras; + extras = extras->next; + err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl); + } + + return(err); + } + +// NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback, +// which may change the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) + { + // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() + if (!sr->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); + +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) + { + mStatus status; + mDNS_Lock(m); + status = uDNS_DeregisterService(m, sr); + mDNS_Unlock(m); + return(status); + } +#endif + if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) + { + debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c); + return(mStatus_BadReferenceErr); + } + else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) + { + debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c); + return(mStatus_NoError); + } + else + { + mDNSu32 i; + mStatus status; + ExtraResourceRecord *e; + mDNS_Lock(m); + e = sr->Extras; + + // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the + // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay + mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat); + mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat); + + mDNS_Deregister_internal(m, &sr->RR_ADV, mDNS_Dereg_normal); + + // We deregister all of the extra records, but we leave the sr->Extras list intact + // in case the client wants to do a RenameAndReregister and reinstate the registration + while (e) + { + mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat); + e = e->next; + } + + for (i=0; i<sr->NumSubTypes; i++) + mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal); + + // Be sure to deregister the PTR last! + // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback, + // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback, + // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure + // we've deregistered all our records and done any other necessary cleanup before that happens. + status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal); + mDNS_Unlock(m); + return(status); + } + } + +// Create a registration that asserts that no such service exists with this name. +// This can be useful where there is a given function is available through several protocols. +// For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP" +// protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an +// "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing +// could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users. +mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context) + { + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context); + if (ConstructServiceName(rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + rr->resrec.rdata->u.srv.priority = 0; + rr->resrec.rdata->u.srv.weight = 0; + rr->resrec.rdata->u.srv.port = zeroIPPort; + if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host); + else rr->HostTarget = mDNStrue; + return(mDNS_Register(m, rr)); + } + +mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, + mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) + { + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + if (!MakeDomainNameFromDNSNameString(rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); + if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); + return(mDNS_Register(m, rr)); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - Startup and Shutdown +#endif + +mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) + { + if (storage && numrecords) + { + mDNSu32 i; + debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity)); + for (i=0; i<numrecords; i++) storage[i].next = &storage[i+1]; + storage[numrecords-1].next = m->rrcache_free; + m->rrcache_free = storage; + m->rrcache_size += numrecords; + } + } + +mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords) + { + mDNS_Lock(m); + mDNS_GrowCache_internal(m, storage, numrecords); + mDNS_Unlock(m); + } + +mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, + CacheEntity *rrcachestorage, mDNSu32 rrcachesize, + mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) + { + mDNSu32 slot; + mDNSs32 timenow; + mStatus result; + + if (!rrcachestorage) rrcachesize = 0; + + m->p = p; + m->KnownBugs = 0; + m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise + m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; + m->mDNSPlatformStatus = mStatus_Waiting; + m->UnicastPort4 = zeroIPPort; + m->UnicastPort6 = zeroIPPort; + m->MainCallback = Callback; + m->MainContext = Context; + m->rec.r.resrec.RecordType = 0; + + // For debugging: To catch and report locking failures + m->mDNS_busy = 0; + m->mDNS_reentrancy = 0; + m->mDNS_shutdown = mDNSfalse; + m->lock_rrcache = 0; + m->lock_Questions = 0; + m->lock_Records = 0; + + // Task Scheduling variables + result = mDNSPlatformTimeInit(); + if (result != mStatus_NoError) return(result); + m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF); + timenow = mDNS_TimeNow_NoLock(m); + + m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section + m->timenow_last = timenow; + m->NextScheduledEvent = timenow; + m->SuppressSending = timenow; + m->NextCacheCheck = timenow + 0x78000000; + m->NextScheduledQuery = timenow + 0x78000000; + m->NextScheduledProbe = timenow + 0x78000000; + m->NextScheduledResponse = timenow + 0x78000000; + m->ExpectUnicastResponse = timenow + 0x78000000; + m->RandomQueryDelay = 0; + m->RandomReconfirmDelay = 0; + m->PktNum = 0; + m->SendDeregistrations = mDNSfalse; + m->SendImmediateAnswers = mDNSfalse; + m->SleepState = mDNSfalse; + + // These fields only required for mDNS Searcher... + m->Questions = mDNSNULL; + m->NewQuestions = mDNSNULL; + m->CurrentQuestion = mDNSNULL; + m->LocalOnlyQuestions = mDNSNULL; + m->NewLocalOnlyQuestions = mDNSNULL; + m->rrcache_size = 0; + m->rrcache_totalused = 0; + m->rrcache_active = 0; + m->rrcache_report = 10; + m->rrcache_free = mDNSNULL; + + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL; + + mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize); + + // Fields below only required for mDNS Responder... + m->hostlabel.c[0] = 0; + m->nicelabel.c[0] = 0; + m->MulticastHostname.c[0] = 0; + m->HIHardware.c[0] = 0; + m->HISoftware.c[0] = 0; + m->ResourceRecords = mDNSNULL; + m->DuplicateRecords = mDNSNULL; + m->NewLocalRecords = mDNSNULL; + m->CurrentRecord = mDNSNULL; + m->HostInterfaces = mDNSNULL; + m->ProbeFailTime = 0; + m->NumFailedProbes = 0; + m->SuppressProbes = 0; + +#ifndef UNICAST_DISABLED + uDNS_Init(m); + m->SuppressStdPort53Queries = 0; +#endif + result = mDNSPlatformInit(m); + + return(result); + } + +mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) + { + m->mDNSPlatformStatus = result; + if (m->MainCallback) + { + mDNS_Lock(m); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + m->MainCallback(m, mStatus_NoError); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + mDNS_Unlock(m); + } + } + +mDNSexport void mDNS_Close(mDNS *const m) + { + mDNSu32 rrcache_active = 0; + mDNSu32 rrcache_totalused = 0; + mDNSu32 slot; + NetworkInterfaceInfo *intf; + mDNS_Lock(m); + + m->mDNS_shutdown = mDNStrue; + +#ifndef UNICAST_DISABLED + uDNS_Close(m); +#endif + rrcache_totalused = m->rrcache_totalused; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + while(m->rrcache_hash[slot]) + { + CacheGroup *cg = m->rrcache_hash[slot]; + while (cg->members) + { + CacheRecord *rr = cg->members; + cg->members = cg->members->next; + if (rr->CRActiveQuestion) rrcache_active++; + ReleaseCacheRecord(m, rr); + } + cg->rrcache_tail = &cg->members; + ReleaseCacheGroup(m, &m->rrcache_hash[slot]); + } + } + debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active); + if (rrcache_active != m->rrcache_active) + LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); + + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) + DeadvertiseInterface(m, intf); + + // Make sure there are nothing but deregistering records remaining in the list + if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) + { + debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name->c); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + } + else + m->CurrentRecord = rr->next; + } + + if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records"); + else debugf("mDNS_Close: No deregistering records remain"); + + // If any deregistering records remain, send their deregistration announcements before we exit + if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m); + else if (m->ResourceRecords) SendResponses(m); + if (m->ResourceRecords) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m, m->ResourceRecords)); + + mDNS_Unlock(m); + debugf("mDNS_Close: mDNSPlatformClose"); + mDNSPlatformClose(m); + debugf("mDNS_Close: done"); + } diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c new file mode 100644 index 0000000000..656b36b774 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c @@ -0,0 +1,152 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + File: mDNSDebug.c + + Contains: Implementation of debugging utilities. Requires a POSIX environment. + + Version: 1.0 + + Change History (most recent first): + +$Log: mDNSDebug.c,v $ +Revision 1.7 2006/08/14 23:24:56 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.6 2005/01/27 22:57:56 cheshire +Fix compile errors on gcc4 + +Revision 1.5 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.4 2004/06/11 22:36:51 cheshire +Fixes for compatibility with Windows + +Revision 1.3 2004/01/28 21:14:23 cheshire +Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) + +Revision 1.2 2003/12/09 01:30:40 rpantos +Fix usage of ARGS... macros to build properly on Windows. + +Revision 1.1 2003/12/08 21:11:42; rpantos +Changes necessary to support mDNSResponder on Linux. + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mDNSDebug.h" + +#include <stdio.h> + +#if defined(WIN32) +// Need to add Windows syslog support here +#define LOG_PID 0x01 +#define LOG_CONS 0x02 +#define LOG_PERROR 0x20 +#define openlog(A,B,C) (void)(A); (void)(B) +#define syslog(A,B,C) +#define closelog() +#else +#include <syslog.h> +#endif + +#include "mDNSEmbeddedAPI.h" + +#if MDNS_DEBUGMSGS +mDNSexport int mDNS_DebugMode = mDNStrue; +#else +mDNSexport int mDNS_DebugMode = mDNSfalse; +#endif + +// Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows +// how to print special data types like IP addresses and length-prefixed domain names +#if MDNS_DEBUGMSGS +mDNSexport void debugf_(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } +#endif + +#if MDNS_DEBUGMSGS > 1 +mDNSexport void verbosedebugf_(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } +#endif + +mDNSlocal void WriteLogMsg(const char *ident, const char *buffer, int logoptflags, int logpriority) + { + if (mDNS_DebugMode) // In debug mode we write to stderr + { + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } + else // else, in production mode, we write to syslog + { + openlog(ident, LOG_PERROR | logoptflags, LOG_DAEMON); + syslog(logpriority, "%s", buffer); + closelog(); + } + } + +// Log message with default "mDNSResponder" ident string at the start +mDNSexport void LogMsg(const char *format, ...) + { + char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + WriteLogMsg("mDNSResponder", buffer, 0, LOG_ERR); + } + +// Log message with specified ident string at the start +mDNSexport void LogMsgIdent(const char *ident, const char *format, ...) + { + char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + WriteLogMsg(ident, buffer, ident && *ident ? LOG_PID : 0, LOG_INFO); + } + +// Log message with no ident string at the start +mDNSexport void LogMsgNoIdent(const char *format, ...) + { + char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + WriteLogMsg("", buffer, 0, LOG_INFO); + } diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h new file mode 100644 index 0000000000..18be95a2b5 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h @@ -0,0 +1,192 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: mDNSDebug.h,v $ +Revision 1.26.2.1 2006/08/29 06:24:22 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.26 2005/07/04 22:40:26 cheshire +Additional debugging code to help catch memory corruption + +Revision 1.25 2004/12/14 21:34:16 cheshire +Add "#define ANSWER_REMOTE_HOSTNAME_QUERIES 0" and comment + +Revision 1.24 2004/09/16 01:58:21 cheshire +Fix compiler warnings + +Revision 1.23 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.22 2004/04/22 04:27:42 cheshire +Spacing tidyup + +Revision 1.21 2004/04/14 23:21:41 ksekar +Removed accidental checkin of MALLOC_DEBUGING flag in 1.20 + +Revision 1.20 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.19 2004/03/15 18:57:59 cheshire +Undo last checkin that accidentally made verbose debugging the default for all targets + +Revision 1.18 2004/03/13 01:57:33 ksekar +<rdar://problem/3192546>: DynDNS: Dynamic update of service records + +Revision 1.17 2004/01/28 21:14:23 cheshire +Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) + +Revision 1.16 2003/12/09 01:30:06 rpantos +Fix usage of ARGS... macros to build properly on Windows. + +Revision 1.15 2003/12/08 20:55:26 rpantos +Move some definitions here from mDNSMacOSX.h. + +Revision 1.14 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + +Revision 1.13 2003/07/02 21:19:46 cheshire +<rdar://problem/3313413> Update copyright notices, etc., in source code comments + +Revision 1.12 2003/05/26 03:01:27 cheshire +<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.11 2003/05/21 17:48:10 cheshire +Add macro to enable GCC's printf format string checking + +Revision 1.10 2003/04/26 02:32:57 cheshire +Add extern void LogMsg(const char *format, ...); + +Revision 1.9 2002/09/21 20:44:49 zarzycki +Added APSL info + +Revision 1.8 2002/09/19 04:20:43 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.7 2002/09/16 18:41:42 cheshire +Merge in license terms from Quinn's copy, in preparation for Darwin release + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __mDNSDebug_h +#define __mDNSDebug_h + +// Set MDNS_DEBUGMSGS to 0 to optimize debugf() calls out of the compiled code +// Set MDNS_DEBUGMSGS to 1 to generate normal debugging messages +// Set MDNS_DEBUGMSGS to 2 to generate verbose debugging messages +// MDNS_DEBUGMSGS is normally set in the project options (or makefile) but can also be set here if desired +// (If you edit the file here to turn on MDNS_DEBUGMSGS while you're debugging some code, be careful +// not to accidentally check-in that change by mistake when you check in your other changes.) + +//#undef MDNS_DEBUGMSGS +//#define MDNS_DEBUGMSGS 2 + +// Set MDNS_CHECK_PRINTF_STYLE_FUNCTIONS to 1 to enable extra GCC compiler warnings +// Note: You don't normally want to do this, because it generates a bunch of +// spurious warnings for the following custom extensions implemented by mDNS_vsnprintf: +// warning: `#' flag used with `%s' printf format (for %#s -- pascal string format) +// warning: repeated `#' flag in format (for %##s -- DNS name string format) +// warning: double format, pointer arg (arg 2) (for %.4a, %.16a, %#a -- IP address formats) +#define MDNS_CHECK_PRINTF_STYLE_FUNCTIONS 0 +#if MDNS_CHECK_PRINTF_STYLE_FUNCTIONS +#define IS_A_PRINTF_STYLE_FUNCTION(F,A) __attribute__ ((format(printf,F,A))) +#else +#define IS_A_PRINTF_STYLE_FUNCTION(F,A) +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#if MDNS_DEBUGMSGS +#define debugf debugf_ +extern void debugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +#else // If debug breaks are off, use a preprocessor trick to optimize those calls out of the code + #if (defined(__GNUC__)) + #define debugf( ARGS... ) ((void)0) + #elif (defined(__MWERKS__)) + #define debugf( ... ) + #else + #define debugf 1 ? ((void)0) : (void) + #endif +#endif + +#if MDNS_DEBUGMSGS > 1 +#define verbosedebugf verbosedebugf_ +extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +#else + #if (defined(__GNUC__)) + #define verbosedebugf( ARGS... ) ((void)0) + #elif (defined(__MWERKS__)) + #define verbosedebugf( ... ) + #else + #define verbosedebugf 1 ? ((void)0) : (void) + #endif +#endif + +// LogMsg is used even in shipping code, to write truly serious error messages to syslog (or equivalent) +extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog +extern void LogMsg(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogMsgIdent(const char *ident, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(2,3); +extern void LogMsgNoIdent(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); + +// Set this symbol to 1 to answer remote queries for our Address, reverse mapping PTR, and HINFO records +#define ANSWER_REMOTE_HOSTNAME_QUERIES 0 + +// Set this symbol to 1 to do extra debug checks on malloc() and free() +// Set this symbol to 2 to write a log message for every malloc() and free() +#define MACOSX_MDNS_MALLOC_DEBUGGING 0 + +#if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 +extern void *mallocL(char *msg, unsigned int size); +extern void freeL(char *msg, void *x); +extern void LogMemCorruption(const char *format, ...); +extern void uds_validatelists(void); +#else +#define mallocL(X,Y) malloc(Y) +#define freeL(X,Y) free(Y) +#endif + +#if MACOSX_MDNS_MALLOC_DEBUGGING >= 2 +#define LogMalloc LogMsg +#else + #if (defined( __GNUC__ )) + #define LogMalloc(ARGS...) ((void)0) + #elif (defined( __MWERKS__ )) + #define LogMalloc( ... ) + #else + #define LogMalloc 1 ? ((void)0) : (void) + #endif +#endif + +#define LogAllOperations 0 + +#if LogAllOperations +#define LogOperation LogMsg +#else +#define LogOperation debugf +#endif + +#define ForceAlerts 0 + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h new file mode 100644 index 0000000000..5ed2c92240 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h @@ -0,0 +1,2850 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + + NOTE: + If you're building an application that uses DNS Service Discovery + this is probably NOT the header file you're looking for. + In most cases you will want to use /usr/include/dns_sd.h instead. + + This header file defines the lowest level raw interface to mDNSCore, + which is appropriate *only* on tiny embedded systems where everything + runs in a single address space and memory is extremely constrained. + All the APIs here are malloc-free, which means that the caller is + responsible for passing in a pointer to the relevant storage that + will be used in the execution of that call, and (when called with + correct parameters) all the calls are guaranteed to succeed. There + is never a case where a call can suffer intermittent failures because + the implementation calls malloc() and sometimes malloc() returns NULL + because memory is so limited that no more is available. + This is primarily for devices that need to have precisely known fixed + memory requirements, with absolutely no uncertainty or run-time variation, + but that certainty comes at a cost of more difficult programming. + + For applications running on general-purpose desktop operating systems + (Mac OS, Linux, Solaris, Windows, etc.) the API you should use is + /usr/include/dns_sd.h, which defines the API by which multiple + independent client processes communicate their DNS Service Discovery + requests to a single "mdnsd" daemon running in the background. + + Even on platforms that don't run multiple independent processes in + multiple independent address spaces, you can still use the preferred + dns_sd.h APIs by linking in "dnssd_clientshim.c", which implements + the standard "dns_sd.h" API calls, allocates any required storage + using malloc(), and then calls through to the low-level malloc-free + mDNSCore routines defined here. This has the benefit that even though + you're running on a small embedded system with a single address space, + you can still use the exact same client C code as you'd use on a + general-purpose desktop system. + + + Change History (most recent first): + +$Log: mDNSEmbeddedAPI.h,v $ +Revision 1.296.2.1 2006/08/29 06:24:22 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.296 2006/06/29 05:28:01 cheshire +Added comment about mDNSlocal and mDNSexport + +Revision 1.295 2006/06/29 03:02:43 cheshire +<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support + +Revision 1.294 2006/06/28 06:50:08 cheshire +In future we may want to change definition of mDNSs32 from "signed long" to "signed int" +I doubt anyone is building mDNSResponder on systems where int is 16-bits, +but lets add a compile-time assertion to make sure. + +Revision 1.293 2006/06/12 18:00:43 cheshire +To make code a little more defensive, check _ILP64 before _LP64, +in case both are set by mistake on some platforms + +Revision 1.292 2006/03/19 17:00:57 cheshire +Define symbol MaxMsg instead of using hard-coded constant value '80' + +Revision 1.291 2006/03/19 02:00:07 cheshire +<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions + +Revision 1.290 2006/03/08 22:42:23 cheshire +Fix spelling mistake: LocalReverseMapomain -> LocalReverseMapDomain + +Revision 1.289 2006/02/26 00:54:41 cheshire +Fixes to avoid code generation warning/error on FreeBSD 7 + +Revision 1.288 2005/12/21 03:24:58 cheshire +<rdar://problem/4388858> Code changes required to compile on EFI + +Revision 1.287 2005/10/20 00:10:33 cheshire +<rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code + +Revision 1.286 2005/09/24 01:09:40 cheshire +Fix comment typos + +Revision 1.285 2005/09/16 20:57:47 cheshire +Add macro mDNS_TimeNow_NoLock(m) to get properly adjusted time without also acquiring lock + +Revision 1.284 2005/07/29 18:04:22 ksekar +<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update + +Revision 1.283 2005/05/13 20:45:09 ksekar +<rdar://problem/4074400> Rapid wide-area txt record updates don't work + +Revision 1.282 2005/03/16 00:42:32 ksekar +<rdar://problem/4012279> Long-lived queries not working on Windows + +Revision 1.281 2005/02/25 17:47:44 ksekar +<rdar://problem/4021868> SendServiceRegistration fails on wake from sleep + +Revision 1.280 2005/02/25 04:21:00 cheshire +<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing + +Revision 1.279 2005/02/17 01:56:14 cheshire +Increase ifname field to 64 bytes + +Revision 1.278 2005/02/09 23:38:51 ksekar +<rdar://problem/3993508> Reregister hostname when DNS server changes but IP address does not + +Revision 1.277 2005/02/09 23:31:12 ksekar +<rdar://problem/3984374> NAT-PMP response callback should return a boolean indicating if the packet matched the request + +Revision 1.276 2005/02/01 19:33:29 ksekar +<rdar://problem/3985239> Keychain format too restrictive + +Revision 1.275 2005/01/27 22:57:55 cheshire +Fix compile errors on gcc4 + +Revision 1.274 2005/01/19 21:01:54 ksekar +<rdar://problem/3955355> uDNS needs to support subtype registration and browsing + +Revision 1.273 2005/01/19 19:15:31 ksekar +Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer + +Revision 1.272 2005/01/18 18:10:55 ksekar +<rdar://problem/3954575> Use 10.4 resolver API to get search domains + +Revision 1.271 2005/01/15 00:56:41 ksekar +<rdar://problem/3954575> Unicast services don't disappear when logging +out of VPN + +Revision 1.270 2005/01/14 18:34:22 ksekar +<rdar://problem/3954571> Services registered outside of firewall don't succeed after location change + +Revision 1.269 2005/01/11 22:50:52 ksekar +Fixed constant naming (was using kLLQ_DefLease for update leases) + +Revision 1.268 2004/12/22 22:25:47 ksekar +<rdar://problem/3734265> NATPMP: handle location changes + +Revision 1.267 2004/12/22 00:13:49 ksekar +<rdar://problem/3873993> Change version, port, and polling interval for LLQ + +Revision 1.266 2004/12/18 03:13:45 cheshire +<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records + +Revision 1.265 2004/12/17 23:37:45 cheshire +<rdar://problem/3485365> Guard against repeating wireless dissociation/re-association +(and other repetitive configuration changes) + +Revision 1.264 2004/12/17 05:25:46 cheshire +<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs + +Revision 1.263 2004/12/16 20:40:25 cheshire +Fix compile warnings + +Revision 1.262 2004/12/16 20:13:00 cheshire +<rdar://problem/3324626> Cache memory management improvements + +Revision 1.261 2004/12/14 21:21:20 ksekar +<rdar://problem/3825979> NAT-PMP: Update response format to contain "Seconds Since Boot" + +Revision 1.260 2004/12/12 23:51:42 ksekar +<rdar://problem/3845683> Wide-area registrations should fallback to using DHCP hostname as target + +Revision 1.259 2004/12/11 20:55:29 ksekar +<rdar://problem/3916479> Clean up registration state machines + +Revision 1.258 2004/12/10 20:48:32 cheshire +<rdar://problem/3705229> Need to pick final EDNS numbers for LLQ and GC + +Revision 1.257 2004/12/10 02:09:23 cheshire +<rdar://problem/3898376> Modify default TTLs + +Revision 1.256 2004/12/09 03:15:40 ksekar +<rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains + +Revision 1.255 2004/12/07 22:48:37 cheshire +Tidying + +Revision 1.254 2004/12/07 21:26:04 ksekar +<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration + +Revision 1.253 2004/12/07 20:42:33 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.252 2004/12/07 03:02:12 ksekar +Fixed comments, grouped unicast-specific routines together + +Revision 1.251 2004/12/06 21:15:22 ksekar +<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.250 2004/12/04 02:12:45 cheshire +<rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.249 2004/12/03 05:18:33 ksekar +<rdar://problem/3810596> mDNSResponder needs to return more specific TSIG errors + +Revision 1.248 2004/12/02 20:03:48 ksekar +<rdar://problem/3889647> Still publishes wide-area domains even after switching to a local subnet + +Revision 1.247 2004/12/01 20:57:19 ksekar +<rdar://problem/3873921> Wide Area Service Discovery must be split-DNS aware + +Revision 1.246 2004/11/29 23:26:32 cheshire +Added NonZeroTime() function, which usually returns the value given, with the exception +that if the value given is zero, it returns one instead. For timer values where zero is +used to mean "not set", this can be used to ensure that setting them to the result of an +interval computation (e.g. "now+interval") does not inadvertently result in a zero value. + +Revision 1.245 2004/11/25 01:28:09 cheshire +<rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too) + +Revision 1.244 2004/11/24 22:00:59 cheshire +Move definition of mDNSAddressIsAllDNSLinkGroup() from mDNSMacOSX.c to mDNSEmbeddedAPI.h + +Revision 1.243 2004/11/23 22:43:53 cheshire +Tidy up code alignment + +Revision 1.242 2004/11/23 03:39:46 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.241 2004/11/22 17:16:19 ksekar +<rdar://problem/3854298> Unicast services don't disappear when you disable all networking + +Revision 1.240 2004/11/19 02:32:43 ksekar +Wide-Area Security: Add LLQ-ID to events + +Revision 1.239 2004/11/15 20:09:23 ksekar +<rdar://problem/3719050> Wide Area support for Add/Remove record + +Revision 1.238 2004/11/12 03:16:48 rpantos +rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName + +Revision 1.237 2004/11/10 20:40:53 ksekar +<rdar://problem/3868216> LLQ mobility fragile on non-primary interface + +Revision 1.236 2004/11/01 20:36:11 ksekar +<rdar://problem/3802395> mDNSResponder should not receive Keychain Notifications + +Revision 1.235 2004/11/01 17:48:14 cheshire +Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but +it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page +137. Since C doesn't have a modular type, we used signed, C's closest approximation. + +Revision 1.234 2004/10/29 21:59:02 ksekar +SOA serial should be a unsigned integer, as per RFC 1035 + +Revision 1.233 2004/10/28 03:24:41 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.232 2004/10/26 06:20:23 cheshire +Add mDNSAddressIsValidNonZero() macro + +Revision 1.231 2004/10/26 06:11:41 cheshire +Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed + +Revision 1.230 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.229 2004/10/25 19:30:52 ksekar +<rdar://problem/3827956> Simplify dynamic host name structures + +Revision 1.228 2004/10/23 01:16:00 cheshire +<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts + +Revision 1.227 2004/10/22 20:52:07 ksekar +<rdar://problem/3799260> Create NAT port mappings for Long Lived Queries + +Revision 1.226 2004/10/20 01:50:40 cheshire +<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API +Implemented ForceMCast mode for AuthRecords as well as for Questions + +Revision 1.225 2004/10/19 21:33:17 cheshire +<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.224 2004/10/16 00:16:59 cheshire +<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check + +Revision 1.223 2004/10/15 23:00:17 ksekar +<rdar://problem/3799242> Need to update LLQs on location changes + +Revision 1.222 2004/10/12 02:49:20 ksekar +<rdar://problem/3831228> Clean up LLQ sleep/wake, error handling + +Revision 1.221 2004/10/10 06:57:15 cheshire +Change definition of "localdomain" to make code compile a little smaller + +Revision 1.220 2004/10/06 01:44:19 cheshire +<rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record + +Revision 1.219 2004/10/03 23:18:58 cheshire +Move address comparison macros from DNSCommon.h to mDNSEmbeddedAPI.h + +Revision 1.218 2004/10/03 23:14:12 cheshire +Add "mDNSEthAddr" type and "zeroEthAddr" constant + +Revision 1.217 2004/09/30 00:24:56 ksekar +<rdar://problem/3695802> Dynamically update default registration domains on config change + +Revision 1.216 2004/09/27 23:24:32 cheshire +Fix typo: SOA refresh interval is supposed to be unsigned + +Revision 1.215 2004/09/26 23:20:35 ksekar +<rdar://problem/3813108> Allow default registrations in multiple wide-area domains + +Revision 1.214 2004/09/25 02:41:39 cheshire +<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events + +Revision 1.213 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.212 2004/09/24 20:57:39 cheshire +<rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors + +Revision 1.211 2004/09/24 20:33:22 cheshire +Remove unused DNSDigest_MD5 declaration + +Revision 1.210 2004/09/23 20:21:07 cheshire +<rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic +Associate a unique sequence number with each received packet, and only increment the count of recent answer +packets if the packet sequence number for this answer record is not one we've already seen and counted. + +Revision 1.209 2004/09/23 20:14:39 cheshire +Rename "question->RecentAnswers" to "question->RecentAnswerPkts" + +Revision 1.208 2004/09/23 00:50:53 cheshire +<rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep + +Revision 1.207 2004/09/22 02:34:46 cheshire +Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h + +Revision 1.206 2004/09/22 00:41:59 cheshire +Move tcp connection status codes into the legal range allocated for mDNS use + +Revision 1.205 2004/09/21 23:40:11 ksekar +<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure + +Revision 1.204 2004/09/21 23:29:50 cheshire +<rdar://problem/3680045> DNSServiceResolve should delay sending packets + +Revision 1.203 2004/09/21 20:58:22 cheshire +Add ifname field to NetworkInterfaceInfo_struct + +Revision 1.202 2004/09/17 00:46:34 cheshire +mDNS_TimeNow should take const mDNS parameter + +Revision 1.201 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.200 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.199 2004/09/16 21:59:16 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.198 2004/09/16 21:36:36 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.197 2004/09/16 00:24:48 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.196 2004/09/14 23:42:35 cheshire +<rdar://problem/3801296> Need to seed random number generator from platform-layer data + +Revision 1.195 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.194 2004/09/10 00:49:57 cheshire +<rdar://problem/3787644> Add error code kDNSServiceErr_Firewall, for future use + +Revision 1.193 2004/09/03 19:23:05 ksekar +<rdar://problem/3788460>: Need retransmission mechanism for wide-area service registrations + +Revision 1.192 2004/09/02 03:48:47 cheshire +<rdar://problem/3709039> Disable targeted unicast query support by default +1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record +2. New field AllowRemoteQuery in AuthRecord structure +3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set +4. mDNS.c only answers remote queries if AllowRemoteQuery is set + +Revision 1.191 2004/08/25 00:37:27 ksekar +<rdar://problem/3774635>: Cleanup DynDNS hostname registration code + +Revision 1.190 2004/08/18 17:35:41 ksekar +<rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.189 2004/08/14 03:22:41 cheshire +<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.188 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.187 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.186 2004/08/13 23:25:00 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + +Revision 1.185 2004/08/12 00:32:36 ksekar +<rdar://problem/3759567>: LLQ Refreshes never terminate if unanswered + +Revision 1.184 2004/08/11 17:09:31 cheshire +Add comment clarifying the applicability of these APIs + +Revision 1.183 2004/08/10 23:19:14 ksekar +<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.182 2004/07/30 17:40:06 ksekar +<rdar://problem/3739115>: TXT Record updates not available for wide-area services + +Revision 1.181 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.180 2004/07/29 02:03:35 ksekar +Delete unused #define and structure field + +Revision 1.179 2004/07/26 22:49:30 ksekar +<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client + +Revision 1.178 2004/07/13 21:24:24 rpantos +Fix for <rdar://problem/3701120>. + +Revision 1.177 2004/06/05 00:04:26 cheshire +<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration + +Revision 1.176 2004/06/04 08:58:29 ksekar +<rdar://problem/3668624>: Keychain integration for secure dynamic update + +Revision 1.175 2004/06/04 00:15:06 cheshire +Move misplaced brackets + +Revision 1.174 2004/06/03 23:30:16 cheshire +Remove extraneous blank lines and white space + +Revision 1.173 2004/06/03 03:09:58 ksekar +<rdar://problem/3668626>: Garbage Collection for Dynamic Updates + +Revision 1.172 2004/06/01 23:46:50 ksekar +<rdar://problem/3675149>: DynDNS: dynamically look up LLQ/Update ports + +Revision 1.171 2004/05/28 23:42:37 ksekar +<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.170 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.169 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.168 2004/05/12 22:03:09 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSEmbeddedAPI.h), implemented to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.167 2004/04/22 04:07:01 cheshire +Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it + +Revision 1.166 2004/04/22 03:15:56 cheshire +Fix use of "struct __attribute__((__packed__))" so it only applies on GCC >= 2.9 + +Revision 1.165 2004/04/22 03:05:28 cheshire +kDNSClass_ANY should be kDNSQClass_ANY + +Revision 1.164 2004/04/21 02:55:03 cheshire +Update comments describing 'InterfaceActive' field + +Revision 1.163 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.162 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.161 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.160 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field + +Revision 1.159 2004/04/09 16:37:15 cheshire +Suggestion from Bob Bradley: +Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers + +Revision 1.158 2004/04/02 19:38:33 cheshire +Update comment about typical RR TTLs + +Revision 1.157 2004/04/02 19:35:53 cheshire +Add clarifying comments about legal mDNSInterfaceID values + +Revision 1.156 2004/04/02 19:19:48 cheshire +Add code to do optional logging of multi-packet KA list time intervals + +Revision 1.155 2004/03/24 00:29:45 ksekar +Make it safe to call StopQuery in a unicast question callback + +Revision 1.154 2004/03/20 01:05:49 cheshire +Test __LP64__ and __ILP64__ to compile properly on a wider range of 64-bit architectures + +Revision 1.153 2004/03/13 01:57:33 ksekar +<rdar://problem/3192546>: DynDNS: Dynamic update of service records + +Revision 1.152 2004/03/09 02:27:16 cheshire +Remove erroneous underscore in 'packed_struct' (makes no difference now, but might in future) + +Revision 1.151 2004/03/02 03:21:56 cheshire +<rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries + +Revision 1.150 2004/02/21 02:06:24 cheshire +Can't use anonymous unions -- they're non-standard and don't work on all compilers + +Revision 1.149 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.148 2004/02/03 19:47:36 ksekar +Added an asynchronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.147 2004/02/03 18:57:35 cheshire +Update comment for "IsLocalDomain()" + +Revision 1.146 2004/01/30 02:20:24 bradley +Map inline to __inline when building with Microsoft C compilers since they do not support C99 inline. + +Revision 1.145 2004/01/29 02:59:17 ksekar +Unicast DNS: Changed from a resource record oriented question/response +matching to packet based matching. New callback architecture allows +collections of records in a response to be processed differently +depending on the nature of the request, and allows the same structure +to be used for internal and client-driven queries with different processing needs. + +Revision 1.144 2004/01/28 20:20:45 ksekar +Unified ActiveQueries and ActiveInternalQueries lists, using a flag to +demux them. Check-in includes work-in-progress code, #ifdef'd out. + +Revision 1.143 2004/01/28 03:41:00 cheshire +<rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries + +Revision 1.142 2004/01/28 02:30:07 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.141 2004/01/27 20:15:22 cheshire +<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 + +Revision 1.140 2004/01/24 23:37:08 cheshire +At Kiren's suggestion, made functions to convert mDNSOpaque16s to/from integer values + +Revision 1.139 2004/01/24 08:46:26 bradley +Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs. + +Revision 1.138 2004/01/24 04:59:15 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.137 2004/01/24 03:40:56 cheshire +Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSEmbeddedAPI.h so clients can use it + +Revision 1.136 2004/01/24 03:38:27 cheshire +Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.135 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.134 2004/01/22 03:54:11 cheshire +Create special meta-interface 'mDNSInterface_ForceMCast' (-2), +which means "do this query via multicast, even if it's apparently a unicast domain" + +Revision 1.133 2004/01/22 03:48:41 cheshire +Make sure uDNS client doesn't accidentally use query ID zero + +Revision 1.132 2004/01/22 03:43:08 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.131 2004/01/21 21:53:18 cheshire +<rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.130 2003/12/14 05:05:29 cheshire +Add comments explaining mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize + +Revision 1.129 2003/12/13 03:05:27 ksekar +<rdar://problem/3192548>: DynDNS: Unicast query of service records + +Revision 1.128 2003/12/01 21:44:23 cheshire +Add mStatus_BadInterfaceErr = -65552 for consistency with dns_sd.h + +Revision 1.127 2003/12/01 18:26:37 cheshire +Also pack the OpaqueXX union types. Otherwise, on some systems, mDNSOpaque16 is four bytes! + +Revision 1.126 2003/12/01 18:23:48 cheshire +<rdar://problem/3464646>: Scalar size problem in mDNS code on some 64-bit architectures + +Revision 1.125 2003/11/22 00:18:27 cheshire +Add compile-time asserts to verify correct sizes of mDNSu32, mDNSOpaque16, etc. + +Revision 1.124 2003/11/20 22:59:54 cheshire +Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h +Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. + +Revision 1.123 2003/11/20 22:53:01 cheshire +Add comment about MAX_ESCAPED_DOMAIN_LABEL + +Revision 1.122 2003/11/20 20:49:53 cheshire +Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures + +Revision 1.121 2003/11/20 05:01:38 cheshire +Update comments; add explanation of Advertise/DontAdvertiseLocalAddresses + +Revision 1.120 2003/11/14 20:59:08 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.119 2003/11/14 19:47:52 cheshire +Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString + +Revision 1.118 2003/11/14 19:18:34 cheshire +Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too + +Revision 1.117 2003/11/08 23:32:24 cheshire +Gave name to anonymous struct, to avoid errors on certain compilers. +(Thanks to ramaprasad.kr@hp.com for reporting this.) + +Revision 1.116 2003/11/07 03:32:56 cheshire +<rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order +This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes +purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is +to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR() +can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place. + +Revision 1.115 2003/09/23 00:53:54 cheshire +NumFailedProbes should be unsigned + +Revision 1.114 2003/08/29 19:44:15 cheshire +<rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears +1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries + that already have at least one unique answer in the cache +2. For these queries, go straight to QM, skipping QU + +Revision 1.113 2003/08/21 19:31:58 cheshire +Cosmetic: Swap order of fields + +Revision 1.112 2003/08/21 19:27:36 cheshire +<rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL + +Revision 1.111 2003/08/21 02:21:50 cheshire +<rdar://problem/3386473> Efficiency: Reduce repeated queries + +Revision 1.110 2003/08/20 23:39:31 cheshire +<rdar://problem/3344098> Review syslog messages, and remove as appropriate + +Revision 1.109 2003/08/19 22:24:10 cheshire +Comment change + +Revision 1.108 2003/08/19 22:20:00 cheshire +<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.107 2003/08/19 06:48:25 cheshire +<rdar://problem/3376552> Guard against excessive record updates +Each record starts with 10 UpdateCredits. +Every update consumes one UpdateCredit. +UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. +As the number of UpdateCredits declines, the number of announcements is similarly scaled back. +When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. + +Revision 1.106 2003/08/19 04:49:28 cheshire +<rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right +1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6. +2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. +3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. + +Revision 1.105 2003/08/19 02:33:37 cheshire +Update comments + +Revision 1.104 2003/08/19 02:31:11 cheshire +<rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries +Final expiration queries now only mark the question for sending on the particular interface +pertaining to the record that's expiring. + +Revision 1.103 2003/08/18 19:05:44 cheshire +<rdar://problem/3382423> UpdateRecord not working right +Added "newrdlength" field to hold new length of updated rdata + +Revision 1.102 2003/08/16 03:39:00 cheshire +<rdar://problem/3338440> InterfaceID -1 indicates "local only" + +Revision 1.101 2003/08/15 20:16:02 cheshire +<rdar://problem/3366590> mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.100 2003/08/14 19:29:04 cheshire +<rdar://problem/3378473> Include cache records in SIGINFO output +Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them + +Revision 1.99 2003/08/14 02:17:05 cheshire +<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.98 2003/08/12 19:56:23 cheshire +Update to APSL 2.0 + +Revision 1.97 2003/08/12 14:59:27 cheshire +<rdar://problem/3374490> Rate-limiting blocks some legitimate responses +When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine +whether to suppress the response, also check LastMCInterface to see if it matches. + +Revision 1.96 2003/08/12 13:57:04 cheshire +<rdar://problem/3323817> Improve cache performance +Changed the number of hash table slots from 37 to 499 + +Revision 1.95 2003/08/09 00:55:02 cheshire +<rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU +Don't scan the whole cache after every packet. + +Revision 1.94 2003/08/09 00:35:29 cheshire + +Revision 1.93 2003/08/08 18:55:48 cheshire +<rdar://problem/3370365> Guard against time going backwards + +Revision 1.92 2003/08/08 18:36:04 cheshire +<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.91 2003/08/06 21:33:39 cheshire +Fix compiler warnings on PocketPC 2003 (Windows CE) + +Revision 1.90 2003/08/06 20:30:17 cheshire +Add structure definition for rdataMX (not currently used, but good to have it for completeness) + +Revision 1.89 2003/08/06 18:58:19 cheshire +Update comments + +Revision 1.88 2003/07/24 23:45:44 cheshire +To eliminate compiler warnings, changed definition of mDNSBool from +"unsigned char" to "int", since "int" is in fact truly the type that C uses +for the result of comparison operators (a<b) and logical operators (a||b) + +Revision 1.87 2003/07/22 23:57:20 cheshire +Move platform-layer function prototypes from mDNSEmbeddedAPI.h to mDNSPlatformFunctions.h where they belong + +Revision 1.86 2003/07/20 03:52:02 ksekar +<rdar://problem/3320722>: Feature: New DNS-SD APIs (#7875) (mDNSResponder component) +Added error type for incompatibility between daemon and client versions + +Revision 1.85 2003/07/19 03:23:13 cheshire +<rdar://problem/2986147> mDNSResponder needs to receive and cache larger records + +Revision 1.84 2003/07/18 23:52:12 cheshire +To improve consistency of field naming, global search-and-replace: +NextProbeTime -> NextScheduledProbe +NextResponseTime -> NextScheduledResponse + +Revision 1.83 2003/07/18 00:29:59 cheshire +<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.82 2003/07/17 17:35:04 cheshire +<rdar://problem/3325583> Rate-limit responses, to guard against packet flooding + +Revision 1.81 2003/07/16 05:01:36 cheshire +Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for +<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass + +Revision 1.80 2003/07/15 01:55:12 cheshire +<rdar://problem/3315777> Need to implement service registration with subtypes + +Revision 1.79 2003/07/13 02:28:00 cheshire +<rdar://problem/3325166> SendResponses didn't all its responses +Delete all references to RRInterfaceActive -- it's now superfluous + +Revision 1.78 2003/07/13 01:47:53 cheshire +Fix one error and one warning in the Windows build + +Revision 1.77 2003/07/11 01:32:38 cheshire +Syntactic cleanup (no change to funcationality): Now that we only have one host name, +rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". + +Revision 1.76 2003/07/11 01:28:00 cheshire +<rdar://problem/3161289> No more local.arpa + +Revision 1.75 2003/07/02 21:19:45 cheshire +<rdar://problem/3313413> Update copyright notices, etc., in source code comments + +Revision 1.74 2003/07/02 02:41:23 cheshire +<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed + +Revision 1.73 2003/06/10 04:24:39 cheshire +<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache +Some additional refinements: +Don't try to do this for unicast-response queries +better tracking of Qs and KAs in multi-packet KA lists + +Revision 1.72 2003/06/10 01:46:27 cheshire +Add better comments explaining how these data structures are intended to be used from the client layer + +Revision 1.71 2003/06/07 06:45:05 cheshire +<rdar://problem/3283666> No need for multiple machines to all be sending the same queries + +Revision 1.70 2003/06/07 04:50:53 cheshire +<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache + +Revision 1.69 2003/06/07 04:22:17 cheshire +Add MsgBuffer for error log and debug messages + +Revision 1.68 2003/06/07 01:46:38 cheshire +<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records + +Revision 1.67 2003/06/07 01:22:14 cheshire +<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function + +Revision 1.66 2003/06/07 00:59:43 cheshire +<rdar://problem/3283454> Need some randomness to spread queries on the network + +Revision 1.65 2003/06/06 21:41:11 cheshire +For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines + +Revision 1.64 2003/06/06 21:38:55 cheshire +Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we +already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) + +Revision 1.63 2003/06/06 17:20:14 cheshire +For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass +(Global search-and-replace; no functional change to code execution.) + +Revision 1.62 2003/06/04 01:25:33 cheshire +<rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages +Display time interval between first and subsequent queries + +Revision 1.61 2003/06/03 05:02:16 cheshire +<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be + +Revision 1.60 2003/05/31 00:09:49 cheshire +<rdar://problem/3274862> Add ability to discover what services are on a network + +Revision 1.59 2003/05/29 06:11:35 cheshire +<rdar://problem/3272214>: Report if there appear to be too many "Resolve" callbacks + +Revision 1.58 2003/05/29 05:48:06 cheshire +Minor fix for when generating printf warnings: mDNS_snprintf arguments are now 3,4 + +Revision 1.57 2003/05/26 03:21:27 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.56 2003/05/26 03:01:27 cheshire +<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.55 2003/05/26 00:47:30 cheshire +Comment clarification + +Revision 1.54 2003/05/24 16:39:48 cheshire +<rdar://problem/3268631> SendResponses also needs to handle multihoming better + +Revision 1.53 2003/05/23 02:15:37 cheshire +Fixed misleading use of the term "duplicate suppression" where it should have +said "known answer suppression". (Duplicate answer suppression is something +different, and duplicate question suppression is yet another thing, so the use +of the completely vague term "duplicate suppression" was particularly bad.) + +Revision 1.52 2003/05/22 02:29:22 cheshire +<rdar://problem/2984918> SendQueries needs to handle multihoming better +Complete rewrite of SendQueries. Works much better now :-) + +Revision 1.51 2003/05/21 20:14:55 cheshire +Fix comments and warnings + +Revision 1.50 2003/05/14 07:08:36 cheshire +<rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.49 2003/05/07 01:49:36 cheshire +Remove "const" in ConstructServiceName prototype + +Revision 1.48 2003/05/07 00:18:44 cheshire +Fix typo: "kDNSQClass_Mask" should be "kDNSClass_Mask" + +Revision 1.47 2003/05/06 00:00:46 cheshire +<rdar://problem/3248914> Rationalize naming of domainname manipulation functions + +Revision 1.46 2003/04/30 20:39:09 cheshire +Add comment + +Revision 1.45 2003/04/29 00:40:50 cheshire +Fix compiler warnings + +Revision 1.44 2003/04/26 02:41:56 cheshire +<rdar://problem/3241281> Change timenow from a local variable to a structure member + +Revision 1.43 2003/04/25 01:45:56 cheshire +<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.42 2003/04/15 20:58:31 jgraessl + +<rdar://problem/3229014> Added a hash to lookup records in the cache. + +Revision 1.41 2003/04/15 18:09:13 jgraessl + +<rdar://problem/3228892> +Reviewed by: Stuart Cheshire +Added code to keep track of when the next cache item will expire so we can +call TidyRRCache only when necessary. + +Revision 1.40 2003/03/29 01:55:19 cheshire +<rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets +Solution: Major cleanup of packet timing and conflict handling rules + +Revision 1.39 2003/03/27 03:30:55 cheshire +<rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.38 2003/03/15 04:40:36 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.37 2003/03/14 21:34:11 cheshire +<rdar://problem/3176950> Can't setup and print to Lexmark PS printers via Airport Extreme +Increase size of cache rdata from 512 to 768 + +Revision 1.36 2003/03/05 03:38:35 cheshire +<rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found! +Fixed by leaving client in list after conflict, until client explicitly deallocates + +Revision 1.35 2003/02/21 02:47:54 cheshire +<rdar://problem/3099194> mDNSResponder needs performance improvements +Several places in the code were calling CacheRRActive(), which searched the entire +question list every time, to see if this cache resource record answers any question. +Instead, we now have a field "CRActiveQuestion" in the resource record structure + +Revision 1.34 2003/02/21 01:54:08 cheshire +<rdar://problem/3099194> mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.33 2003/02/20 06:48:32 cheshire +<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations +Reviewed by: Josh Graessley, Bob Bradley + +Revision 1.32 2003/01/31 03:35:59 cheshire +<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results +When there were *two* active questions in the list, they were incorrectly +finding *each other* and *both* being marked as duplicates of another question + +Revision 1.31 2003/01/29 02:46:37 cheshire +Fix for IPv6: +A physical interface is identified solely by its InterfaceID (not by IP and type). +On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. +In cases where the requested outbound protocol (v4 or v6) is not supported on +that InterfaceID, the platform support layer should simply discard that packet. + +Revision 1.30 2003/01/29 01:47:08 cheshire +Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity + +Revision 1.29 2003/01/28 05:23:43 cheshire +<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results +Add 'Active' flag for interfaces + +Revision 1.28 2003/01/28 01:35:56 cheshire +Revise comment about ThisQInterval to reflect new semantics + +Revision 1.27 2003/01/13 23:49:42 jgraessl +Merged changes for the following fixes in to top of tree: +<rdar://problem/3086540> computer name changes not handled properly +<rdar://problem/3124348> service name changes are not properly handled +<rdar://problem/3124352> announcements sent in pairs, failing chattiness test + +Revision 1.26 2002/12/23 22:13:28 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.25 2002/09/21 20:44:49 zarzycki +Added APSL info + +Revision 1.24 2002/09/19 23:47:35 cheshire +Added mDNS_RegisterNoSuchService() function for assertion of non-existence +of a particular named service + +Revision 1.23 2002/09/19 21:25:34 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.22 2002/09/19 04:20:43 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.21 2002/09/17 01:06:35 cheshire +Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() + +Revision 1.20 2002/09/16 18:41:41 cheshire +Merge in license terms from Quinn's copy, in preparation for Darwin release + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __mDNSClientAPI_h +#define __mDNSClientAPI_h + +#if defined(EFI32) || defined(EFI64) +// EFI doesn't have stdarg.h +#include "Tiano.h" +#define va_list VA_LIST +#define va_start(a, b) VA_START(a, b) +#define va_end(a) VA_END(a) +#define va_arg(a, b) VA_ARG(a, b) +#else +#include <stdarg.h> // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration +#endif + +#include "mDNSDebug.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// *************************************************************************** +// Function scope indicators + +// If you see "mDNSlocal" before a function name in a C file, it means the function is not callable outside this file +#ifndef mDNSlocal +#define mDNSlocal static +#endif +// If you see "mDNSexport" before a symbol in a C file, it means the symbol is exported for use by clients +// For every "mDNSexport" in a C file, there needs to be a corresponding "extern" declaration in some header file +// (When a C file #includes a header file, the "extern" declarations tell the compiler: +// "This symbol exists -- but not necessarily in this C file.") +#ifndef mDNSexport +#define mDNSexport +#endif + +// Explanation: These local/export markers are a little habit of mine for signaling the programmers' intentions. +// When "mDNSlocal" is just a synonym for "static", and "mDNSexport" is a complete no-op, you could be +// forgiven for asking what purpose they serve. The idea is that if you see "mDNSexport" in front of a +// function definition it means the programmer intended it to be exported and callable from other files +// in the project. If you see "mDNSlocal" in front of a function definition it means the programmer +// intended it to be private to that file. If you see neither in front of a function definition it +// means the programmer forgot (so you should work out which it is supposed to be, and fix it). +// Using "mDNSlocal" instead of "static" makes it easier to do a textual searches for one or the other. +// For example you can do a search for "static" to find if any functions declare any local variables as "static" +// (generally a bad idea unless it's also "const", because static storage usually risks being non-thread-safe) +// without the results being cluttered with hundreds of matches for functions declared static. +// - Stuart Cheshire + +// *************************************************************************** +// Structure packing macro + +// If we're not using GNUC, it's not fatal. +// Most compilers naturally pack the on-the-wire structures correctly anyway, so a plain "struct" is usually fine. +// In the event that structures are not packed correctly, mDNS_Init() will detect this and report an error, so the +// developer will know what's wrong, and can investigate what needs to be done on that compiler to provide proper packing. +#ifndef packedstruct + #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #define packedstruct struct __attribute__((__packed__)) + #define packedunion union __attribute__((__packed__)) + #else + #define packedstruct struct + #define packedunion union + #endif +#endif + +// *************************************************************************** +#if 0 +#pragma mark - DNS Resource Record class and type constants +#endif + +typedef enum // From RFC 1035 + { + kDNSClass_IN = 1, // Internet + kDNSClass_CS = 2, // CSNET + kDNSClass_CH = 3, // CHAOS + kDNSClass_HS = 4, // Hesiod + kDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] + + kDNSClass_Mask = 0x7FFF,// Multicast DNS uses the bottom 15 bits to identify the record class... + kDNSClass_UniqueRRSet = 0x8000,// ... and the top bit indicates that all other cached records are now invalid + + kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" + kDNSQClass_UnicastResponse = 0x8000 // Top bit set in a question means "unicast response acceptable" + } DNS_ClassValues; + +typedef enum // From RFC 1035 + { + kDNSType_A = 1, // 1 Address + kDNSType_NS, // 2 Name Server + kDNSType_MD, // 3 Mail Destination + kDNSType_MF, // 4 Mail Forwarder + kDNSType_CNAME, // 5 Canonical Name + kDNSType_SOA, // 6 Start of Authority + kDNSType_MB, // 7 Mailbox + kDNSType_MG, // 8 Mail Group + kDNSType_MR, // 9 Mail Rename + kDNSType_NULL, // 10 NULL RR + kDNSType_WKS, // 11 Well-known-service + kDNSType_PTR, // 12 Domain name pointer + kDNSType_HINFO, // 13 Host information + kDNSType_MINFO, // 14 Mailbox information + kDNSType_MX, // 15 Mail Exchanger + kDNSType_TXT, // 16 Arbitrary text string + + kDNSType_AAAA = 28, // 28 IPv6 address + kDNSType_SRV = 33, // 33 Service record + kDNSType_OPT = 41, // EDNS0 OPT record + kDNSType_TSIG = 250, // 250 Transaction Signature + + kDNSQType_ANY = 255 // Not a DNS type, but a DNS query type, meaning "all types" + } DNS_TypeValues; + +// *************************************************************************** +#if 0 +#pragma mark - Simple types +#endif + +// mDNS defines its own names for these common types to simplify portability across +// multiple platforms that may each have their own (different) names for these types. +typedef int mDNSBool; +typedef signed char mDNSs8; +typedef unsigned char mDNSu8; +typedef signed short mDNSs16; +typedef unsigned short mDNSu16; + +// <http://gcc.gnu.org/onlinedocs/gcc-3.3.3/cpp/Common-Predefined-Macros.html> says +// __LP64__ _LP64 +// These macros are defined, with value 1, if (and only if) the compilation is +// for a target where long int and pointer both use 64-bits and int uses 32-bit. +// <http://www.intel.com/software/products/compilers/clin/docs/ug/lin1077.htm> says +// Macro Name __LP64__ Value 1 +// A quick Google search for "defined(__LP64__)" OR "#ifdef __LP64__" gives 2590 hits and +// a search for "#if __LP64__" gives only 12, so I think we'll go with the majority and use defined() +#if defined(_ILP64) || defined(__ILP64__) +typedef signed int32 mDNSs32; +typedef unsigned int32 mDNSu32; +#elif defined(_LP64) || defined(__LP64__) +typedef signed int mDNSs32; +typedef unsigned int mDNSu32; +#else +typedef signed long mDNSs32; +typedef unsigned long mDNSu32; +//typedef signed int mDNSs32; +//typedef unsigned int mDNSu32; +#endif + +// To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct +// This way, mDNSInterfaceIDs can be assigned, and compared with each other, but not with other types +// Declaring the type to be the typical generic "void *" would lack this type checking +typedef struct mDNSInterfaceID_dummystruct { void *dummy; } *mDNSInterfaceID; + +// These types are for opaque two- and four-byte identifiers. +// The "NotAnInteger" fields of the unions allow the value to be conveniently passed around in a +// register for the sake of efficiency, and compared for equality or inequality, but don't forget -- +// just because it is in a register doesn't mean it is an integer. Operations like greater than, +// less than, add, multiply, increment, decrement, etc., are undefined for opaque identifiers, +// and if you make the mistake of trying to do those using the NotAnInteger field, then you'll +// find you get code that doesn't work consistently on big-endian and little-endian machines. +typedef packedunion { mDNSu8 b[ 2]; mDNSu16 NotAnInteger; } mDNSOpaque16; +typedef packedunion { mDNSu8 b[ 4]; mDNSu32 NotAnInteger; } mDNSOpaque32; +typedef packedunion { mDNSu8 b[ 6]; mDNSu16 w[3]; mDNSu32 l[1]; } mDNSOpaque48; +typedef packedunion { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128; + +typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) +typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identifier (not an integer) +typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) +typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque identifier (not an integer) + +enum + { + mDNSAddrType_None = 0, + mDNSAddrType_IPv4 = 4, + mDNSAddrType_IPv6 = 6, + mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording + }; + +typedef struct + { + mDNSs32 type; + union { mDNSv6Addr v6; mDNSv4Addr v4; } ip; + } mDNSAddr; + +enum { mDNSfalse = 0, mDNStrue = 1 }; + +#define mDNSNULL 0L + +enum + { + mStatus_Waiting = 1, + mStatus_NoError = 0, + + // mDNS return values are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537) + // The top end of the range (FFFE FFFF) is used for error codes; + // the bottom end of the range (FFFE FF00) is used for non-error values; + + // Error codes: + mStatus_UnknownErr = -65537, // First value: 0xFFFE FFFF + mStatus_NoSuchNameErr = -65538, + mStatus_NoMemoryErr = -65539, + mStatus_BadParamErr = -65540, + mStatus_BadReferenceErr = -65541, + mStatus_BadStateErr = -65542, + mStatus_BadFlagsErr = -65543, + mStatus_UnsupportedErr = -65544, + mStatus_NotInitializedErr = -65545, + mStatus_NoCache = -65546, + mStatus_AlreadyRegistered = -65547, + mStatus_NameConflict = -65548, + mStatus_Invalid = -65549, + mStatus_Firewall = -65550, + mStatus_Incompatible = -65551, + mStatus_BadInterfaceErr = -65552, + mStatus_Refused = -65553, + mStatus_NoSuchRecord = -65554, + mStatus_NoAuth = -65555, + mStatus_NoSuchKey = -65556, + mStatus_NATTraversal = -65557, + mStatus_DoubleNAT = -65558, + mStatus_BadTime = -65559, + mStatus_BadSig = -65560, // while we define this per RFC 2845, BIND 9 returns Refused for bad/missing signatures + mStatus_BadKey = -65561, + mStatus_TransientErr = -65562, // transient failures, e.g. sending packets shortly after a network transition or wake from sleep + // -65563 to -65786 currently unused; available for allocation + + // tcp connection status + mStatus_ConnPending = -65787, + mStatus_ConnFailed = -65788, + mStatus_ConnEstablished = -65789, + + // Non-error values: + mStatus_GrowCache = -65790, + mStatus_ConfigChanged = -65791, + mStatus_MemFree = -65792 // Last value: 0xFFFE FF00 + + // mStatus_MemFree is the last legal mDNS error code, at the end of the range allocated for mDNS + }; + +typedef mDNSs32 mStatus; + +// RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters +#define MAX_DOMAIN_LABEL 63 +typedef struct { mDNSu8 c[ 64]; } domainlabel; // One label: length byte and up to 63 characters + +// RFC 1034/1035 specify that a domain name, including length bytes, data bytes, and terminating zero, may be up to 255 bytes long +#define MAX_DOMAIN_NAME 255 +typedef struct { mDNSu8 c[256]; } domainname; // Up to 255 bytes of length-prefixed domainlabels + +typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string + +// The longest legal textual form of a DNS name is 1005 bytes, including the C-string terminating NULL at the end. +// Explanation: +// When a native domainname object is converted to printable textual form using ConvertDomainNameToCString(), +// non-printing characters are represented in the conventional DNS way, as '\ddd', where ddd is a three-digit decimal number. +// The longest legal domain name is 255 bytes, in the form of four labels as shown below: +// Length byte, 63 data bytes, length byte, 63 data bytes, length byte, 63 data bytes, length byte, 61 data bytes, zero byte. +// Each label is encoded textually as characters followed by a trailing dot. +// If every character has to be represented as a four-byte escape sequence, then this makes the maximum textual form four labels +// plus the C-string terminating NULL as shown below: +// 63*4+1 + 63*4+1 + 63*4+1 + 61*4+1 + 1 = 1005. +// Note that MAX_ESCAPED_DOMAIN_LABEL is not normally used: If you're only decoding a single label, escaping is usually not required. +// It is for domain names, where dots are used as label separators, that proper escaping is vital. +#define MAX_ESCAPED_DOMAIN_LABEL 254 +#define MAX_ESCAPED_DOMAIN_NAME 1005 + +// Most records have a TTL of 75 minutes, so that their 80% cache-renewal query occurs once per hour. +// For records containing a hostname (in the name on the left, or in the rdata on the right), +// like A, AAAA, reverse-mapping PTR, and SRV, we use a two-minute TTL by default, because we don't want +// them to hang around for too long in the cache if the host in question crashes or otherwise goes away. +// Wide-area service discovery records have a very short TTL to avoid poluting intermediate caches with +// dynamic records. When discovered via Long Lived Queries (with change notifications), resource record +// TTLs can be safely ignored. + +#define kStandardTTL (3600UL * 100 / 80) +#define kHostNameTTL 120UL +#define kWideAreaTTL 3 + +#define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL) + +// *************************************************************************** +#if 0 +#pragma mark - DNS Message structures +#endif + +#define mDNS_numZones numQuestions +#define mDNS_numPrereqs numAnswers +#define mDNS_numUpdates numAuthorities + +typedef packedstruct + { + mDNSOpaque16 id; + mDNSOpaque16 flags; + mDNSu16 numQuestions; + mDNSu16 numAnswers; + mDNSu16 numAuthorities; + mDNSu16 numAdditionals; + } DNSMessageHeader; + +// We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used) +// However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet +// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total +#define AbsoluteMaxDNSMessageData 8940 +#define NormalMaxDNSMessageData 1440 +typedef packedstruct + { + DNSMessageHeader h; // Note: Size 12 bytes + mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 + } DNSMessage; + +// *************************************************************************** +#if 0 +#pragma mark - Resource Record structures +#endif + +// Authoritative Resource Records: +// There are four basic types: Shared, Advisory, Unique, Known Unique + +// * Shared Resource Records do not have to be unique +// -- Shared Resource Records are used for DNS-SD service PTRs +// -- It is okay for several hosts to have RRs with the same name but different RDATA +// -- We use a random delay on responses to reduce collisions when all the hosts respond to the same query +// -- These RRs typically have moderately high TTLs (e.g. one hour) +// -- These records are announced on startup and topology changes for the benefit of passive listeners +// -- These records send a goodbye packet when deregistering +// +// * Advisory Resource Records are like Shared Resource Records, except they don't send a goodbye packet +// +// * Unique Resource Records should be unique among hosts within any given mDNS scope +// -- The majority of Resource Records are of this type +// -- If two entities on the network have RRs with the same name but different RDATA, this is a conflict +// -- Responses may be sent immediately, because only one host should be responding to any particular query +// -- These RRs typically have low TTLs (e.g. a few minutes) +// -- On startup and after topology changes, a host issues queries to verify uniqueness + +// * Known Unique Resource Records are treated like Unique Resource Records, except that mDNS does +// not have to verify their uniqueness because this is already known by other means (e.g. the RR name +// is derived from the host's IP or Ethernet address, which is already known to be a unique identifier). + +// Summary of properties of different record types: +// Probe? Does this record type send probes before announcing? +// Conflict? Does this record type react if we observe an apparent conflict? +// Goodbye? Does this record type send a goodbye packet on departure? +// +// Probe? Conflict? Goodbye? Notes +// Unregistered Should not appear in any list (sanity check value) +// Shared No No Yes e.g. Service PTR record +// Deregistering No No Yes Shared record about to announce its departure and leave the list +// Advisory No No No +// Unique Yes Yes No Record intended to be unique -- will probe to verify +// Verified Yes Yes No Record has completed probing, and is verified unique +// KnownUnique No Yes No Record is assumed by other means to be unique + +// Valid lifecycle of a record: +// Unregistered -> Shared -> Deregistering -(goodbye)-> Unregistered +// Unregistered -> Advisory -> Unregistered +// Unregistered -> Unique -(probe)-> Verified -> Unregistered +// Unregistered -> KnownUnique -> Unregistered + +// Each Authoritative kDNSRecordType has only one bit set. This makes it easy to quickly see if a record +// is one of a particular set of types simply by performing the appropriate bitwise masking operation. + +// Cache Resource Records (received from the network): +// There are four basic types: Answer, Unique Answer, Additional, Unique Additional +// Bit 7 (the top bit) of kDNSRecordType is always set for Cache Resource Records; always clear for Authoritative Resource Records +// Bit 6 (value 0x40) is set for answer records; clear for additional records +// Bit 5 (value 0x20) is set for records received with the kDNSClass_UniqueRRSet + +enum + { + kDNSRecordTypeUnregistered = 0x00, // Not currently in any list + kDNSRecordTypeDeregistering = 0x01, // Shared record about to announce its departure and leave the list + + kDNSRecordTypeUnique = 0x02, // Will become a kDNSRecordTypeVerified when probing is complete + + kDNSRecordTypeAdvisory = 0x04, // Like Shared, but no goodbye packet + kDNSRecordTypeShared = 0x08, // Shared means record name does not have to be unique -- use random delay on responses + + kDNSRecordTypeVerified = 0x10, // Unique means mDNS should check that name is unique (and then send immediate responses) + kDNSRecordTypeKnownUnique = 0x20, // Known Unique means mDNS can assume name is unique without checking + // For Dynamic Update records, Known Unique means the record must already exist on the server. + kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + + kDNSRecordTypePacketAdd = 0x80, // Received in the Additional Section of a DNS Response + kDNSRecordTypePacketAddUnique = 0x90, // Received in the Additional Section of a DNS Response with kDNSClass_UniqueRRSet set + kDNSRecordTypePacketAuth = 0xA0, // Received in the Authorities Section of a DNS Response + kDNSRecordTypePacketAuthUnique = 0xB0, // Received in the Authorities Section of a DNS Response with kDNSClass_UniqueRRSet set + kDNSRecordTypePacketAns = 0xC0, // Received in the Answer Section of a DNS Response + kDNSRecordTypePacketAnsUnique = 0xD0, // Received in the Answer Section of a DNS Response with kDNSClass_UniqueRRSet set + + kDNSRecordTypePacketAnsMask = 0x40, // True for PacketAns and PacketAnsUnique + kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique + }; + +typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; +typedef packedstruct { mDNSu16 preference; domainname exchange; } rdataMX; +typedef packedstruct + { + domainname mname; + domainname rname; + mDNSs32 serial; // Modular counter; increases when zone changes + mDNSu32 refresh; // Time in seconds that a slave waits after successful replication of the database before it attempts replication again + mDNSu32 retry; // Time in seconds that a slave waits after an unsuccessful replication attempt before it attempts replication again + mDNSu32 expire; // Time in seconds that a slave holds on to old data while replication attempts remain unsuccessful + mDNSu32 min; // Nominally the minimum record TTL for this zone, in seconds; also used for negative caching. + } rdataSOA; + +typedef packedstruct + { + mDNSu16 vers; + mDNSu16 llqOp; + mDNSu16 err; + mDNSu8 id[8]; + mDNSu32 lease; + } LLQOptData; + +#define LLQ_OPTLEN ((3 * sizeof(mDNSu16)) + 8 + sizeof(mDNSu32)) +// Windows adds pad bytes to sizeof(LLQOptData). Use this macro when setting length fields or validating option rdata from +// off the wire. Use sizeof(LLQOptData) when dealing with structures (e.g. memcpy). Never memcpy between on-the-wire +// representation and a structure + +// NOTE: rdataOpt format may be repeated an arbitrary number of times in a single resource record +typedef packedstruct + { + mDNSu16 opt; + mDNSu16 optlen; + union { LLQOptData llq; mDNSu32 lease; } OptData; + } rdataOpt; + +// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record +// MaximumRDSize is 8K the absolute maximum we support (at least for now) +#define StandardAuthRDSize 264 +#define MaximumRDSize 8192 + +// InlineCacheRDSize is 68 +// Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object +// Records received from the network with rdata larger than this have additional storage allocated for the rdata +// A quick unscientific sample from a busy network at Apple with lots of machines revealed this: +// 1461 records in cache +// 292 were one-byte TXT records +// 136 were four-byte A records +// 184 were sixteen-byte AAAA records +// 780 were various PTR, TXT and SRV records from 12-64 bytes +// Only 69 records had rdata bigger than 64 bytes +// Note that since CacheRecord object and a CacheGroup object are allocated out of the same pool, it's sensible to +// have them both be the same size. Making one smaller without making the other smaller won't actually save any memory. +#define InlineCacheRDSize 68 + +#define InlineCacheGroupNameSize 144 + +typedef union + { + mDNSu8 data[StandardAuthRDSize]; + mDNSv4Addr ipv4; // For 'A' record + mDNSv6Addr ipv6; // For 'AAAA' record + domainname name; // For PTR, NS, and CNAME records + UTF8str255 txt; // For TXT record + rdataSRV srv; // For SRV record + rdataMX mx; // For MX record + rdataSOA soa; // For SOA record + rdataOpt opt; // For eDNS0 opt record + } RDataBody; + +typedef struct + { + mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody)) + RDataBody u; + } RData; +#define sizeofRDataHeader (sizeof(RData) - sizeof(RDataBody)) + +typedef struct AuthRecord_struct AuthRecord; +typedef struct CacheRecord_struct CacheRecord; +typedef struct CacheGroup_struct CacheGroup; +typedef struct DNSQuestion_struct DNSQuestion; +typedef struct mDNS_struct mDNS; +typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; +typedef struct NATTraversalInfo_struct NATTraversalInfo; + +// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef void mDNSRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result); + +// Note: +// Restrictions: An mDNSRecordUpdateCallback may not make any mDNS API calls. +// The intent of this callback is to allow the client to free memory, if necessary. +// The internal data structures of the mDNS code may not be in a state where mDNS API calls may be made safely. +typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData); + +typedef struct + { + mDNSu8 RecordType; // See enum above + mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface + // For records received off the wire, InterfaceID is *always* set to the receiving interface + // For our authoritative records, InterfaceID is usually zero, except for those few records + // that are interface-specific (e.g. address records, especially linklocal addresses) + domainname *name; + mDNSu16 rrtype; + mDNSu16 rrclass; + mDNSu32 rroriginalttl; // In seconds + mDNSu16 rdlength; // Size of the raw rdata, in bytes + mDNSu16 rdestimate; // Upper bound on size of rdata after name compression + mDNSu32 namehash; // Name-based (i.e. case-insensitive) hash of name + mDNSu32 rdatahash; // For rdata containing domain name (e.g. PTR, SRV, CNAME etc.), case-insensitive name hash + // else, for all other rdata, 32-bit hash of the raw rdata + // Note: This requirement is important. Various routines like AddAdditionalsToResponseList(), + // ReconfirmAntecedents(), etc., use rdatahash as a pre-flight check to see + // whether it's worth doing a full SameDomainName() call. If the rdatahash + // is not a correct case-insensitive name hash, they'll get false negatives. + RData *rdata; // Pointer to storage for this rdata + } ResourceRecord; + +// Unless otherwise noted, states may apply to either independent record registrations or service registrations +typedef enum + { + regState_FetchingZoneData = 1, // getting info - update not sent + regState_Pending = 2, // update sent, reply not received + regState_Registered = 3, // update sent, reply received + regState_DeregPending = 4, // dereg sent, reply not received + regState_DeregDeferred = 5, // dereg requested while in Pending state - send dereg AFTER registration is confirmed + regState_Cancelled = 6, // update not sent, reg. cancelled by client + regState_Unregistered = 8, // not in any list + regState_Refresh = 9, // outstanding refresh (or target change) message + regState_NATMap = 10, // establishing NAT port mapping or learning public address + regState_UpdatePending = 11, // update in flight as result of mDNS_Update call + regState_NoTarget = 12, // service registration pending registration of hostname (ServiceRegistrations only) + regState_ExtraQueued = 13, // extra record to be registered upon completion of service registration (RecordRegistrations only) + regState_NATError = 14 // unable to complete NAT traversal + } regState_t; + +// context for both ServiceRecordSet and individual AuthRec structs +typedef struct + { + // registration/lease state + regState_t state; + mDNSBool lease; // dynamic update contains (should contain) lease option + mDNSs32 expire; // expiration of lease (-1 for static) + mDNSBool TestForSelfConflict; // on name conflict, check if we're just seeing our own orphaned records + + // identifier to match update request and response + mDNSOpaque16 id; + + // server info + domainname zone; // the zone that is updated + mDNSAddr ns; // primary name server for the record's zone !!!KRS not technically correct to cache longer than TTL + mDNSIPPort port; // port on which server accepts dynamic updates + + // NAT traversal context + NATTraversalInfo *NATinfo; // may be NULL + + // state for deferred operations + mDNSBool ClientCallbackDeferred; // invoke client callback on completion of pending operation(s) + mStatus DeferredStatus; // status to deliver when above flag is set + mDNSBool SRVUpdateDeferred; // do we need to change target or port once current operation completes? + mDNSBool SRVChanged; // temporarily deregistered service because its SRV target or port changed + + // uDNS_UpdateRecord support fields + RData *OrigRData; mDNSu16 OrigRDLen; // previously registered, being deleted + RData *InFlightRData; mDNSu16 InFlightRDLen; // currently being registered + RData *QueuedRData; mDNSu16 QueuedRDLen; // if the client call Update while an update is in flight, we must finish the + // pending operation (re-transmitting if necessary) THEN register the queued update + mDNSRecordUpdateCallback *UpdateRDCallback; // client callback to free old rdata + } uDNS_RegInfo; + +struct AuthRecord_struct + { + // For examples of how to set up this structure for use in mDNS_Register(), + // see mDNS_AdvertiseInterface() or mDNS_RegisterService(). + // Basically, resrec and persistent metadata need to be set up before calling mDNS_Register(). + // mDNS_SetupResourceRecord() is avaliable as a helper routine to set up most fields to sensible default values for you + + AuthRecord *next; // Next in list; first element of structure for efficiency reasons + // Field Group 1: Common ResourceRecord fields + ResourceRecord resrec; + uDNS_RegInfo uDNS_info; + + // Field Group 2: Persistent metadata for Authoritative Records + AuthRecord *Additional1; // Recommended additional record to include in response + AuthRecord *Additional2; // Another additional + AuthRecord *DependentOn; // This record depends on another for its uniqueness checking + AuthRecord *RRSet; // This unique record is part of an RRSet + mDNSRecordCallback *RecordCallback; // Callback function to call for state changes, and to free memory asynchronously on deregistration + void *RecordContext; // Context parameter for the callback function + mDNSu8 HostTarget; // Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name + mDNSu8 AllowRemoteQuery; // Set if we allow hosts not on the local link to query this record + mDNSu8 ForceMCast; // Set by client to advertise solely via multicast, even for apparently unicast names + + // Field Group 3: Transient state for Authoritative Records + mDNSu8 Acknowledged; // Set if we've given the success callback to the client + mDNSu8 ProbeCount; // Number of probes remaining before this record is valid (kDNSRecordTypeUnique) + mDNSu8 AnnounceCount; // Number of announcements remaining (kDNSRecordTypeShared) + mDNSu8 RequireGoodbye; // Set if this RR has been announced on the wire and will require a goodbye packet + mDNSu8 LocalAnswer; // Set if this RR has been delivered to LocalOnly questions + mDNSu8 IncludeInProbe; // Set if this RR is being put into a probe right now + mDNSInterfaceID ImmedAnswer; // Someone on this interface issued a query we need to answer (all-ones for all interfaces) + mDNSu8 ImmedUnicast; // Set if we may send our response directly via unicast to the requester +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + mDNSs32 ImmedAnswerMarkTime; +#endif + mDNSInterfaceID ImmedAdditional; // Hint that we might want to also send this record, just to be helpful + mDNSInterfaceID SendRNow; // The interface this query is being sent on right now + mDNSv4Addr v4Requester; // Recent v4 query for this record, or all-ones if more than one recent query + mDNSv6Addr v6Requester; // Recent v6 query for this record, or all-ones if more than one recent query + AuthRecord *NextResponse; // Link to the next element in the chain of responses to generate + const mDNSu8 *NR_AnswerTo; // Set if this record was selected by virtue of being a direct answer to a question + AuthRecord *NR_AdditionalTo; // Set if this record was selected by virtue of being additional to another + mDNSs32 ThisAPInterval; // In platform time units: Current interval for announce/probe + mDNSs32 AnnounceUntil; // In platform time units: Creation time + TTL + mDNSs32 LastAPTime; // In platform time units: Last time we sent announcement/probe + mDNSs32 LastMCTime; // Last time we multicast this record (used to guard against packet-storm attacks) + mDNSInterfaceID LastMCInterface; // Interface this record was multicast on at the time LastMCTime was recorded + RData *NewRData; // Set if we are updating this record with new rdata + mDNSu16 newrdlength; // ... and the length of the new RData + mDNSRecordUpdateCallback *UpdateCallback; + mDNSu32 UpdateCredits; // Token-bucket rate limiting of excessive updates + mDNSs32 NextUpdateCredit; // Time next token is added to bucket + mDNSs32 UpdateBlocked; // Set if update delaying is in effect + + domainname namestorage; + RData rdatastorage; // Normally the storage is right here, except for oversized records + // rdatastorage MUST be the last thing in the structure -- when using oversized AuthRecords, extra bytes + // are appended after the end of the AuthRecord, logically augmenting the size of the rdatastorage + // DO NOT ADD ANY MORE FIELDS HERE + }; + +// Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field +typedef struct ARListElem + { + struct ARListElem *next; + AuthRecord ar; // Note: Must be last struct in field to accomodate oversized AuthRecords + } ARListElem; + +struct CacheGroup_struct // Header object for a list of CacheRecords with the same name + { + CacheGroup *next; // Next CacheGroup object in this hash table bucket + mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name + CacheRecord *members; // List of CacheRecords with this same name + CacheRecord **rrcache_tail; // Tail end of that list + domainname *name; // Common name for all CacheRecords in this list + mDNSu8 namestorage[InlineCacheGroupNameSize]; + }; + +struct CacheRecord_struct + { + CacheRecord *next; // Next in list; first element of structure for efficiency reasons + ResourceRecord resrec; + + // Transient state for Cache Records + CacheRecord *NextInKAList; // Link to the next element in the chain of known answers to send + mDNSs32 TimeRcvd; // In platform time units + mDNSs32 DelayDelivery; // Set if we want to defer delivery of this answer to local clients + mDNSs32 NextRequiredQuery; // In platform time units + mDNSs32 LastUsed; // In platform time units + DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer + mDNSu32 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer + mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries + mDNSu32 MPUnansweredQ; // Multi-packet query handling: Number of times we've seen a query for this record + mDNSs32 MPLastUnansweredQT; // Multi-packet query handling: Last time we incremented MPUnansweredQ + mDNSu32 MPUnansweredKA; // Multi-packet query handling: Number of times we've seen this record in a KA list + mDNSBool MPExpectingKA; // Multi-packet query handling: Set when we increment MPUnansweredQ; allows one KA + CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set + + struct { mDNSu16 MaxRDLength; mDNSu8 data[InlineCacheRDSize]; } rdatastorage; // Storage for small records is right here + }; + +// Storage sufficient to hold either a CacheGroup header or a CacheRecord +typedef union CacheEntity_union CacheEntity; +union CacheEntity_union { CacheEntity *next; CacheGroup cg; CacheRecord cr; }; + +typedef struct + { + CacheRecord r; + mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes + domainname namestorage; // Needs to go *after* the extra rdata bytes + } LargeCacheRecord; + +typedef struct uDNS_HostnameInfo + { + struct uDNS_HostnameInfo *next; + domainname fqdn; + AuthRecord *arv4; // registered IPv4 address record + AuthRecord *arv6; // registered IPv6 address record + mDNSRecordCallback *StatusCallback; // callback to deliver success or error code to client layer + const void *StatusContext; // Client Context + } uDNS_HostnameInfo; + +enum + { + DNSServer_Untested = 0, + DNSServer_Failed = 1, + DNSServer_Passed = 2 + }; + +typedef struct DNSServer + { + struct DNSServer *next; + mDNSAddr addr; + mDNSBool del; // Set when we're planning to delete this from the list + mDNSu32 teststate; // Have we sent bug-detection query to this server? + domainname domain; // name->server matching for "split dns" + } DNSServer; + +typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; + +// A NetworkInterfaceInfo_struct serves two purposes: +// 1. It holds the address, PTR and HINFO records to advertise a given IP address on a given physical interface +// 2. It tells mDNSCore which physical interfaces are available; each physical interface has its own unique InterfaceID. +// Since there may be multiple IP addresses on a single physical interface, +// there may be multiple NetworkInterfaceInfo_structs with the same InterfaceID. +// In this case, to avoid sending the same packet n times, when there's more than one +// struct with the same InterfaceID, mDNSCore picks one member of the set to be the +// active representative of the set; all others have the 'InterfaceActive' flag unset. + +struct NetworkInterfaceInfo_struct + { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + NetworkInterfaceInfo *next; + + mDNSBool InterfaceActive; // Set if interface is sending & receiving packets (see comment above) + mDNSBool IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID + mDNSBool IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID + + // Standard AuthRecords that every Responder host should have (one per active IP address) + AuthRecord RR_A; // 'A' or 'AAAA' (address) record for our ".local" name + AuthRecord RR_PTR; // PTR (reverse lookup) record + AuthRecord RR_HINFO; + + // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() + mDNSInterfaceID InterfaceID; // Identifies physical interface; MUST NOT be 0, -1, or -2 + mDNSAddr ip; // The IPv4 or IPv6 address to advertise + mDNSAddr mask; + char ifname[64]; // Windows uses a GUID string for the interface name, which doesn't fit in 16 bytes + mDNSBool Advertise; // False if you are only searching on this interface + mDNSBool McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? + }; + +typedef struct ExtraResourceRecord_struct ExtraResourceRecord; +struct ExtraResourceRecord_struct + { + ExtraResourceRecord *next; + mDNSu32 ClientID; // Opaque ID field to be used by client to map an AddRecord call to a set of Extra records + AuthRecord r; + // Note: Add any additional fields *before* the AuthRecord in this structure, not at the end. + // In some cases clients can allocate larger chunks of memory and set r->rdata->MaxRDLength to indicate + // that this extra memory is available, which would result in any fields after the AuthRecord getting smashed + }; + +// Note: Within an mDNSServiceCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef struct ServiceRecordSet_struct ServiceRecordSet; +typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result); +struct ServiceRecordSet_struct + { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_RegisterService(); + // all required data is passed as parameters to that function. + ServiceRecordSet *next; + uDNS_RegInfo uDNS_info; + mDNSServiceCallback *ServiceCallback; + void *ServiceContext; + ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration + mDNSu32 NumSubTypes; + AuthRecord *SubTypes; + mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict + domainname Host; // Set if this service record does not use the standard target host name + AuthRecord RR_ADV; // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local. + AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. + AuthRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target + AuthRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName + // Don't add any fields after AuthRecord RR_TXT. + // This is where the implicit extra space goes if we allocate a ServiceRecordSet containing an oversized RR_TXT record + }; + +// *************************************************************************** +#if 0 +#pragma mark - Question structures +#endif + +// We record the last eight instances of each duplicate query +// This gives us v4/v6 on each of Ethernet/AirPort and Firewire, and two free slots "for future expansion" +// If the host has more active interfaces that this it is not fatal -- duplicate question suppression will degrade gracefully. +// Since we will still remember the last eight, the busiest interfaces will still get the effective duplicate question suppression. +#define DupSuppressInfoSize 8 + +typedef struct + { + mDNSs32 Time; + mDNSInterfaceID InterfaceID; + mDNSs32 Type; // v4 or v6? + } DupSuppressInfo; + +typedef enum + { + // Setup states + LLQ_UnInit = 0, + LLQ_GetZoneInfo = 1, + LLQ_InitialRequest = 2, + LLQ_SecondaryRequest = 3, + LLQ_Refresh = 4, + LLQ_Retry = 5, + LLQ_Established = 6, + LLQ_Suspended = 7, + LLQ_SuspendDeferred = 8, // suspend once we get zone info + LLQ_SuspendedPoll = 9, // suspended from polling state + LLQ_NatMapWait = 10, + + // Established/error states + LLQ_Static = 16, + LLQ_Poll = 17, + LLQ_Error = 18, + LLQ_Cancelled = 19 + } LLQ_State; + +typedef struct + { + LLQ_State state; + mDNSAddr servAddr; + mDNSIPPort servPort; + DNSQuestion *question; + mDNSu32 origLease; // seconds (relative) + mDNSs32 retry; // ticks (absolute) + mDNSs32 expire; // ticks (absolute) + mDNSs16 ntries; + mDNSu8 id[8]; + mDNSBool deriveRemovesOnResume; + mDNSBool NATMap; // does this LLQ use the global LLQ NAT mapping? + } LLQ_Info; + +// LLQ constants +#define kDNSOpt_LLQ 1 +#define kDNSOpt_Lease 2 +#define kLLQ_Vers 1 +#define kLLQ_DefLease 7200 // 2 hours +#define kLLQ_MAX_TRIES 3 // retry an operation 3 times max +#define kLLQ_INIT_RESEND 2 // resend an un-ack'd packet after 2 seconds, then double for each additional +#define kLLQ_DEF_RETRY 1800 // retry a failed operation after 30 minutes +// LLQ Operation Codes +#define kLLQOp_Setup 1 +#define kLLQOp_Refresh 2 +#define kLLQOp_Event 3 + +#define LLQ_OPT_RDLEN ((2 * sizeof(mDNSu16)) + LLQ_OPTLEN) +#define LEASE_OPT_RDLEN (2 * sizeof(mDNSu16)) + sizeof(mDNSs32) + +// LLQ Errror Codes +enum + { + LLQErr_NoError = 0, + LLQErr_ServFull = 1, + LLQErr_Static = 2, + LLQErr_FormErr = 3, + LLQErr_NoSuchLLQ = 4, + LLQErr_BadVers = 5, + LLQErr_UnknownErr = 6 + }; + +typedef void (*InternalResponseHndlr)(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *internalContext); +typedef struct + { + mDNSOpaque16 id; + mDNSBool internal; + InternalResponseHndlr responseCallback; // NULL if internal field is false + LLQ_Info *llq; // NULL for 1-shot queries + mDNSBool Answered; // have we received an answer (including NXDOMAIN) for this question? + CacheRecord *knownAnswers; + mDNSs32 RestartTime; // Mark when we restart a suspended query + void *context; + } uDNS_QuestionInfo; + +// Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +struct DNSQuestion_struct + { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + DNSQuestion *next; + mDNSu32 qnamehash; + mDNSs32 DelayAnswering; // Set if we want to defer answering this question until the cache settles + mDNSs32 LastQTime; // Last scheduled transmission of this Q on *all* applicable interfaces + mDNSs32 ThisQInterval; // LastQTime + ThisQInterval is the next scheduled transmission of this Q + // ThisQInterval > 0 for an active question; + // ThisQInterval = 0 for a suspended question that's still in the list + // ThisQInterval = -1 for a cancelled question that's been removed from the list + mDNSs32 LastAnswerPktNum; // The sequence number of the last response packet containing an answer to this Q + mDNSu32 RecentAnswerPkts; // Number of answers since the last time we sent this query + mDNSu32 CurrentAnswers; // Number of records currently in the cache that answer this question + mDNSu32 LargeAnswers; // Number of answers with rdata > 1024 bytes + mDNSu32 UniqueAnswers; // Number of answers received with kDNSClass_UniqueRRSet bit set + mDNSInterfaceID FlappingInterface;// Set when an interface goes away, to flag if removes are delivered for this Q + DNSQuestion *DuplicateOf; + DNSQuestion *NextInDQList; + DupSuppressInfo DupSuppress[DupSuppressInfoSize]; + mDNSInterfaceID SendQNow; // The interface this query is being sent on right now + mDNSBool SendOnAll; // Set if we're sending this question on all active interfaces + mDNSu32 RequestUnicast; // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set + mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces + uDNS_QuestionInfo uDNS_info; + + // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() + mDNSInterfaceID InterfaceID; // Non-zero if you want to issue queries only on a single specific IP interface + mDNSAddr Target; // Non-zero if you want to direct queries to a specific unicast target address + mDNSIPPort TargetPort; // Must be set if Target is set + mDNSOpaque16 TargetQID; // Must be set if Target is set + domainname qname; + mDNSu16 qtype; + mDNSu16 qclass; + mDNSBool LongLived; // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer. + mDNSBool ExpectUnique; // Set by client if it's expecting unique RR(s) for this question, not shared RRs + mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names + mDNSBool ReturnCNAME; // Set by client to request callbacks for intermediate CNAME records + mDNSQuestionCallback *QuestionCallback; + void *QuestionContext; + }; + +typedef struct + { + // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() + // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. + domainname name; + mDNSInterfaceID InterfaceID; // ID of the interface the response was received on + mDNSAddr ip; // Remote (destination) IP address where this service can be accessed + mDNSIPPort port; // Port where this service can be accessed + mDNSu16 TXTlen; + mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) + } ServiceInfo; + +// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef struct ServiceInfoQuery_struct ServiceInfoQuery; +typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); +struct ServiceInfoQuery_struct + { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); + // all required data is passed as parameters to that function. + // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information + // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may + // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. + DNSQuestion qSRV; + DNSQuestion qTXT; + DNSQuestion qAv4; + DNSQuestion qAv6; + mDNSu8 GotSRV; + mDNSu8 GotTXT; + mDNSu8 GotADD; + mDNSu32 Answers; + ServiceInfo *info; + mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; + void *ServiceInfoQueryContext; + }; + +// *************************************************************************** +#if 0 +#pragma mark - NAT Traversal structures and constants +#endif + +#define NATMAP_INIT_RETRY (mDNSPlatformOneSecond / 4) // start at 250ms w/ exponential decay +#define NATMAP_MAX_RETRY mDNSPlatformOneSecond // back off to once per second +#define NATMAP_MAX_TRIES 3 // for max 3 tries +#define NATMAP_DEFAULT_LEASE (60 * 60) // lease life in seconds +#define NATMAP_VERS 0 +#define NATMAP_RESPONSE_MASK 0x80 + +typedef enum + { + NATOp_AddrRequest = 0, + NATOp_MapUDP = 1, + NATOp_MapTCP = 2 + } NATOp_t; + +enum + { + NATErr_None = 0, + NATErr_Vers = 1, + NATErr_Refused = 2, + NATErr_NetFail = 3, + NATErr_Res = 4, + NATErr_Opcode = 5 + }; + +typedef mDNSu16 NATErr_t; + +typedef enum + { + NATState_Init = 0, + NATState_Request = 1, + NATState_Established = 2, + NATState_Legacy = 3, + NATState_Error = 4, + NATState_Refresh = 5, + NATState_Deleted = 6 + } NATState_t; +// Note: we have no explicit "cancelled" state, where a service/interface is deregistered while we + // have an outstanding NAT request. This is conveyed by the "reg" pointer being set to NULL + +typedef packedstruct + { + mDNSu8 vers; + mDNSu8 opcode; + } NATAddrRequest; + +typedef packedstruct + { + mDNSu8 vers; + mDNSu8 opcode; + mDNSOpaque16 err; + mDNSOpaque32 uptime; + mDNSv4Addr PubAddr; + } NATAddrReply; + +typedef packedstruct + { + mDNSu8 vers; + mDNSu8 opcode; + mDNSOpaque16 unused; + mDNSIPPort priv; + mDNSIPPort pub; + mDNSOpaque32 lease; + } NATPortMapRequest; + +typedef packedstruct + { + mDNSu8 vers; + mDNSu8 opcode; + mDNSOpaque16 err; + mDNSOpaque32 uptime; + mDNSIPPort priv; + mDNSIPPort pub; + mDNSOpaque32 lease; + } NATPortMapReply; + +// Pass NULL for pkt on error (including timeout) +typedef mDNSBool (*NATResponseHndlr)(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len); + +struct NATTraversalInfo_struct + { + NATOp_t op; + NATResponseHndlr ReceiveResponse; + union { AuthRecord *RecordRegistration; ServiceRecordSet *ServiceRegistration; } reg; + mDNSAddr Router; + mDNSIPPort PublicPort; + union { NATAddrRequest AddrReq; NATPortMapRequest PortReq; } request; + mDNSs32 retry; // absolute time when we retry + mDNSs32 RetryInterval; // delta between time sent and retry + int ntries; + NATState_t state; + NATTraversalInfo *next; + }; + +// *************************************************************************** +#if 0 +#pragma mark - Main mDNS object, used to hold all the mDNS state +#endif + +typedef void mDNSCallback(mDNS *const m, mStatus result); + +#define CACHE_HASH_SLOTS 499 + +enum + { + mDNS_KnownBug_PhantomInterfaces = 1 + }; + +typedef struct + { + mDNSs32 nextevent; + DNSQuestion *ActiveQueries; //!!!KRS this should be a hashtable (hash on messageID) + DNSQuestion *CurrentQuery; // pointer to ActiveQueries list being examined in a loop. Functions that remove + // elements from the ActiveQueries list must update this pointer (if non-NULL) as necessary. + //!!!KRS do the same for registration lists + ServiceRecordSet *ServiceRegistrations; + AuthRecord *RecordRegistrations; + NATTraversalInfo *NATTraversals; + mDNSu16 NextMessageID; + DNSServer *Servers; // list of DNS servers + mDNSAddr Router; + mDNSAddr AdvertisedV4; // IPv4 address pointed to by hostname + mDNSAddr MappedV4; // Cache of public address if PrimaryIP is behind a NAT + mDNSAddr AdvertisedV6; // IPv6 address pointed to by hostname + NATTraversalInfo *LLQNatInfo; // Nat port mapping to receive LLQ events + domainname ServiceRegDomain; // (going away w/ multi-user support) + struct uDNS_AuthInfo *AuthInfoList; // list of domains requiring authentication for updates. + uDNS_HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata + DNSQuestion ReverseMap; // Reverse-map query to find static hostname for service target + mDNSBool ReverseMapActive; // Is above query active? + domainname StaticHostname; // Current answer to reverse-map query (above) + mDNSBool DelaySRVUpdate; // Delay SRV target/port update to avoid "flap" + mDNSs32 NextSRVUpdate; // Time to perform delayed update + } uDNS_GlobalInfo; + +struct mDNS_struct + { + // Internal state fields. These hold the main internal state of mDNSCore; + // the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_Init(); + // all required data is passed as parameters to that function. + + mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size + mDNSu32 KnownBugs; + mDNSBool CanReceiveUnicastOn5353; + mDNSBool AdvertiseLocalAddresses; + mStatus mDNSPlatformStatus; + mDNSIPPort UnicastPort4; + mDNSIPPort UnicastPort6; + mDNSCallback *MainCallback; + void *MainContext; + + // For debugging: To catch and report locking failures + mDNSu32 mDNS_busy; // Incremented between mDNS_Lock/mDNS_Unlock section + mDNSu32 mDNS_reentrancy; // Incremented when calling a client callback + mDNSu8 mDNS_shutdown; // Set when we're shutting down, allows us to skip some unnecessary steps + mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified + mDNSu8 lock_Questions; + mDNSu8 lock_Records; + #define MaxMsg 120 + char MsgBuffer[MaxMsg]; // Temp storage used while building error log messages + + // Task Scheduling variables + mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards + mDNSs32 timenow; // The time that this particular activation of the mDNS code started + mDNSs32 timenow_last; // The time the last time we ran + mDNSs32 NextScheduledEvent; // Derived from values below + mDNSs32 SuppressSending; // Don't send *any* packets during this time + mDNSs32 NextCacheCheck; // Next time to refresh cache record before it expires + mDNSs32 NextScheduledQuery; // Next time to send query in its exponential backoff sequence + mDNSs32 NextScheduledProbe; // Next time to probe for new authoritative record + mDNSs32 NextScheduledResponse; // Next time to send authoritative record(s) in responses + mDNSs32 ExpectUnicastResponse; // Set when we send a query with the kDNSQClass_UnicastResponse bit set + mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire + mDNSu32 RandomReconfirmDelay; // For de-synchronization of reconfirmation queries on the wire + mDNSs32 PktNum; // Unique sequence number assigned to each received packet + mDNSBool SendDeregistrations; // Set if we need to send deregistrations (immediately) + mDNSBool SendImmediateAnswers; // Set if we need to send answers (immediately -- or as soon as SuppressSending clears) + mDNSBool SleepState; // Set if we're sleeping (send no more packets) + + // These fields only required for mDNS Searcher... + DNSQuestion *Questions; // List of all registered questions, active and inactive + DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache + DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() + DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly + DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only questions not yet answered + mDNSu32 rrcache_size; // Total number of available cache entries + mDNSu32 rrcache_totalused; // Number of cache entries currently occupied + mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions + mDNSu32 rrcache_report; + CacheEntity *rrcache_free; + CacheGroup *rrcache_hash[CACHE_HASH_SLOTS]; + + // Fields below only required for mDNS Responder... + domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8 + domainlabel hostlabel; // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules + domainname MulticastHostname; // Fully Qualified "dot-local" Host Name, e.g. "Foo.local." + UTF8str255 HIHardware; + UTF8str255 HISoftware; + AuthRecord *ResourceRecords; + AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records + AuthRecord *NewLocalRecords; // Fresh local-only records not yet delivered to local-only questions + AuthRecord *CurrentRecord; // Next AuthRecord about to be examined + NetworkInterfaceInfo *HostInterfaces; + mDNSs32 ProbeFailTime; + mDNSu32 NumFailedProbes; + mDNSs32 SuppressProbes; + + // unicast-specific data + uDNS_GlobalInfo uDNS_info; + mDNSs32 SuppressStdPort53Queries; // Wait before allowing the next standard unicast query to the user's configured DNS server + + // Fixed storage, to avoid creating large objects on the stack + DNSMessage imsg; // Incoming message received from wire + DNSMessage omsg; // Outgoing message we're building + LargeCacheRecord rec; // Resource Record extracted from received message + }; + +#define FORALL_CACHERECORDS(SLOT,CG,CR) \ + for ((SLOT) = 0; (SLOT) < CACHE_HASH_SLOTS; (SLOT)++) \ + for((CG)=m->rrcache_hash[(SLOT)]; (CG); (CG)=(CG)->next) \ + for ((CR) = (CG)->members; (CR); (CR)=(CR)->next) + +// *************************************************************************** +#if 0 +#pragma mark - Useful Static Constants +#endif + +extern const mDNSIPPort zeroIPPort; +extern const mDNSv4Addr zerov4Addr; +extern const mDNSv6Addr zerov6Addr; +extern const mDNSEthAddr zeroEthAddr; +extern const mDNSv4Addr onesIPv4Addr; +extern const mDNSv6Addr onesIPv6Addr; +extern const mDNSAddr zeroAddr; + +extern const mDNSInterfaceID mDNSInterface_Any; // Zero +extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value + +extern const mDNSIPPort UnicastDNSPort; +extern const mDNSIPPort NATPMPPort; +extern const mDNSIPPort DNSEXTPort; +extern const mDNSIPPort MulticastDNSPort; +extern const mDNSIPPort LoopbackIPCPort; + +extern const mDNSv4Addr AllDNSAdminGroup; +#define AllDNSLinkGroupv4 (AllDNSLinkGroup_v4.ip.v4) +#define AllDNSLinkGroupv6 (AllDNSLinkGroup_v6.ip.v6) +extern const mDNSAddr AllDNSLinkGroup_v4; +extern const mDNSAddr AllDNSLinkGroup_v6; + +extern const mDNSOpaque16 zeroID; +extern const mDNSOpaque16 QueryFlags; +extern const mDNSOpaque16 uQueryFlags; +extern const mDNSOpaque16 ResponseFlags; +extern const mDNSOpaque16 UpdateReqFlags; +extern const mDNSOpaque16 UpdateRespFlags; + +#define localdomain (*(const domainname *)"\x5" "local") +#define LocalReverseMapDomain (*(const domainname *)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa") + +// *************************************************************************** +#if 0 +#pragma mark - Inline functions +#endif + +#if (defined(_MSC_VER)) + #define mDNSinline static __inline +#elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #define mDNSinline static inline +#endif + +// If we're not doing inline functions, then this header needs to have the extern declarations +#if !defined(mDNSinline) +extern mDNSs32 NonZeroTime(mDNSs32 t); +extern mDNSu16 mDNSVal16(mDNSOpaque16 x); +extern mDNSu32 mDNSVal32(mDNSOpaque32 x); +extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v); +extern mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v); +#endif + +// If we're compiling the particular C file that instantiates our inlines, then we +// define "mDNSinline" (to empty string) so that we generate code in the following section +#if (!defined(mDNSinline) && mDNS_InstantiateInlines) +#define mDNSinline +#endif + +#ifdef mDNSinline + +mDNSinline mDNSs32 NonZeroTime(mDNSs32 t) { if (t) return(t); else return(1); } + +mDNSinline mDNSu16 mDNSVal16(mDNSOpaque16 x) { return((mDNSu16)((mDNSu16)x.b[0] << 8 | (mDNSu16)x.b[1])); } +mDNSinline mDNSu32 mDNSVal32(mDNSOpaque32 x) { return((mDNSu32)((mDNSu32)x.b[0] << 24 | (mDNSu32)x.b[1] << 16 | (mDNSu32)x.b[2] << 8 | (mDNSu32)x.b[3])); } + +mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) + { + mDNSOpaque16 x; + x.b[0] = (mDNSu8)(v >> 8); + x.b[1] = (mDNSu8)(v & 0xFF); + return(x); + } + +mDNSinline mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v) + { + mDNSOpaque32 x; + x.b[0] = (mDNSu8) (v >> 24) ; + x.b[1] = (mDNSu8)((v >> 16) & 0xFF); + x.b[2] = (mDNSu8)((v >> 8 ) & 0xFF); + x.b[3] = (mDNSu8)((v ) & 0xFF); + return x; + } + +#endif + +// *************************************************************************** +#if 0 +#pragma mark - Main Client Functions +#endif + +// Every client should call mDNS_Init, passing in storage for the mDNS object and the mDNS_PlatformSupport object. +// +// Clients that are only advertising services should use mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize. +// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, mDNS_StartResolveService, etc.) +// need to provide storage for the resource record cache, or the query calls will return 'mStatus_NoCache'. +// The rrcachestorage parameter is the address of memory for the resource record cache, and +// the rrcachesize parameter is the number of entries in the CacheRecord array passed in. +// (i.e. the size of the cache memory needs to be sizeof(CacheRecord) * rrcachesize). +// OS X 10.3 Panther uses an initial cache size of 64 entries, and then mDNSCore sends an +// mStatus_GrowCache message if it needs more. +// +// Most clients should use mDNS_Init_AdvertiseLocalAddresses. This causes mDNSCore to automatically +// create the correct address records for all the hosts interfaces. If you plan to advertise +// services being offered by the local machine, this is almost always what you want. +// There are two cases where you might use mDNS_Init_DontAdvertiseLocalAddresses: +// 1. A client-only device, that browses for services but doesn't advertise any of its own. +// 2. A proxy-registration service, that advertises services being offered by other machines, and takes +// the appropriate steps to manually create the correct address records for those other machines. +// In principle, a proxy-like registration service could manually create address records for its own machine too, +// but this would be pointless extra effort when using mDNS_Init_AdvertiseLocalAddresses does that for you. +// +// When mDNS has finished setting up the client's callback is called +// A client can also spin and poll the mDNSPlatformStatus field to see when it changes from mStatus_Waiting to mStatus_NoError +// +// Call mDNS_Close to tidy up before exiting +// +// Call mDNS_Register with a completed AuthRecord object to register a resource record +// If the resource record type is kDNSRecordTypeUnique (or kDNSknownunique) then if a conflicting resource record is discovered, +// the resource record's mDNSRecordCallback will be called with error code mStatus_NameConflict. The callback should deregister +// the record, and may then try registering the record again after picking a new name (e.g. by automatically appending a number). +// Following deregistration, the RecordCallback will be called with result mStatus_MemFree to signal that it is safe to deallocate +// the record's storage (memory must be freed asynchronously to allow for goodbye packets and dynamic update deregistration). +// +// Call mDNS_StartQuery to initiate a query. mDNS will proceed to issue Multicast DNS query packets, and any time a response +// is received containing a record which matches the question, the DNSQuestion's mDNSAnswerCallback function will be called +// Call mDNS_StopQuery when no more answers are required +// +// Care should be taken on multi-threaded or interrupt-driven environments. +// The main mDNS routines call mDNSPlatformLock() on entry and mDNSPlatformUnlock() on exit; +// each platform layer needs to implement these appropriately for its respective platform. +// For example, if the support code on a particular platform implements timer callbacks at interrupt time, then +// mDNSPlatformLock/Unlock need to disable interrupts or do similar concurrency control to ensure that the mDNS +// code is not entered by an interrupt-time timer callback while in the middle of processing a client call. + +extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, + CacheEntity *rrcachestorage, mDNSu32 rrcachesize, + mDNSBool AdvertiseLocalAddresses, + mDNSCallback *Callback, void *Context); +// See notes above on use of NoCache/ZeroCacheSize +#define mDNS_Init_NoCache mDNSNULL +#define mDNS_Init_ZeroCacheSize 0 +// See notes above on use of Advertise/DontAdvertiseLocalAddresses +#define mDNS_Init_AdvertiseLocalAddresses mDNStrue +#define mDNS_Init_DontAdvertiseLocalAddresses mDNSfalse +#define mDNS_Init_NoInitCallback mDNSNULL +#define mDNS_Init_NoInitCallbackContext mDNSNULL + +extern void mDNS_GrowCache (mDNS *const m, CacheEntity *storage, mDNSu32 numrecords); +extern void mDNS_Close (mDNS *const m); +extern mDNSs32 mDNS_Execute (mDNS *const m); + +extern mStatus mDNS_Register (mDNS *const m, AuthRecord *const rr); +extern mStatus mDNS_Update (mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback); +extern mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr); + +extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr); +extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr); +extern mDNSs32 mDNS_TimeNow(const mDNS *const m); + +// *************************************************************************** +#if 0 +#pragma mark - Platform support functions that are accessible to the client layer too +#endif + +extern mDNSs32 mDNSPlatformOneSecond; + +// *************************************************************************** +#if 0 +#pragma mark - General utility and helper functions +#endif + +// mDNS_RegisterService is a single call to register the set of resource records associated with a given named service. +// +// mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery, +// to find the IP address, port number, and demultiplexing information for a given named service. +// As with mDNS_StartQuery, it executes asynchronously, and calls the ServiceInfoQueryCallback when the answer is +// found. After the service is resolved, the client should call mDNS_StopResolveService to complete the transaction. +// The client can also call mDNS_StopResolveService at any time to abort the transaction. +// +// mDNS_AddRecordToService adds an additional record to a Service Record Set. This record may be deregistered +// via mDNS_RemoveRecordFromService, or by deregistering the service. mDNS_RemoveRecordFromService is passed a +// callback to free the memory associated with the extra RR when it is safe to do so. The ExtraResourceRecord +// object can be found in the record's context pointer. + +// mDNS_GetBrowseDomains is a special case of the mDNS_StartQuery call, where the resulting answers +// are a list of PTR records indicating (in the rdata) domains that are recommended for browsing. +// After getting the list of domains to browse, call mDNS_StopQuery to end the search. +// mDNS_GetDefaultBrowseDomain returns the name of the domain that should be highlighted by default. +// +// mDNS_GetRegistrationDomains and mDNS_GetDefaultRegistrationDomain are the equivalent calls to get the list +// of one or more domains that should be offered to the user as choices for where they may register their service, +// and the default domain in which to register in the case where the user has made no selection. + +extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, + mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context); + +extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context); +extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl); +extern mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context); +extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname); +extern mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr); + +extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context); +#define mDNS_DeregisterNoSuchService mDNS_Deregister + +extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, + const domainname *const srv, const domainname *const domain, + const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context); +#define mDNS_StopBrowse mDNS_StopQuery + +extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); +extern void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query); + +typedef enum + { + mDNS_DomainTypeBrowse = 0, + mDNS_DomainTypeBrowseDefault = 1, + mDNS_DomainTypeBrowseLegacy = 2, + mDNS_DomainTypeRegistration = 3, + mDNS_DomainTypeRegistrationDefault = 4, + + mDNS_DomainTypeMax = 4 + } mDNS_DomainType; + +extern const char *const mDNS_DomainTypeNames[]; + +extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); +#define mDNS_StopGetDomains mDNS_StopQuery +extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname); +#define mDNS_StopAdvertiseDomains mDNS_Deregister + +// *************************************************************************** +#if 0 +#pragma mark - DNS name utility functions +#endif + +// In order to expose the full capabilities of the DNS protocol (which allows any arbitrary eight-bit values +// in domain name labels, including unlikely characters like ascii nulls and even dots) all the mDNS APIs +// work with DNS's native length-prefixed strings. For convenience in C, the following utility functions +// are provided for converting between C's null-terminated strings and DNS's length-prefixed strings. + +// Assignment +// A simple C structure assignment of a domainname can cause a protection fault by accessing unmapped memory, +// because that object is defined to be 256 bytes long, but not all domainname objects are truly the full size. +// This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid. +#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC)->c, (DST)->c, DomainNameLength((SRC))) + +// Comparison functions +extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); +extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); +extern mDNSBool IsLocalDomain(const domainname *d); // returns true for domains that by default should be looked up using link-local multicast + +// Get total length of domain name, in native DNS format, including terminal root label +// (e.g. length of "com." is 5 (length byte, three data bytes, final zero) +extern mDNSu16 DomainNameLength(const domainname *const name); + +// Append functions to append one or more labels to an existing native format domain name: +// AppendLiteralLabelString adds a single label from a literal C string, with no escape character interpretation. +// AppendDNSNameString adds zero or more labels from a C string using conventional DNS dots-and-escaping interpretation +// AppendDomainLabel adds a single label from a native format domainlabel +// AppendDomainName adds zero or more labels from a native format domainname +extern mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr); +extern mDNSu8 *AppendDNSNameString (domainname *const name, const char *cstr); +extern mDNSu8 *AppendDomainLabel (domainname *const name, const domainlabel *const label); +extern mDNSu8 *AppendDomainName (domainname *const name, const domainname *const append); + +// Convert from null-terminated string to native DNS format: +// The DomainLabel form makes a single label from a literal C string, with no escape character interpretation. +// The DomainName form makes native format domain name from a C string using conventional DNS interpretation: +// dots separate labels, and within each label, '\.' represents a literal dot, '\\' represents a literal +// backslash and backslash with three decimal digits (e.g. \000) represents an arbitrary byte value. +extern mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr); +extern mDNSu8 *MakeDomainNameFromDNSNameString (domainname *const name, const char *cstr); + +// Convert native format domainlabel or domainname back to C string format +// IMPORTANT: +// When using ConvertDomainLabelToCString, the target buffer must be MAX_ESCAPED_DOMAIN_LABEL (254) bytes long +// to guarantee there will be no buffer overrun. It is only safe to use a buffer shorter than this in rare cases +// where the label is known to be constrained somehow (for example, if the label is known to be either "_tcp" or "_udp"). +// Similarly, when using ConvertDomainNameToCString, the target buffer must be MAX_ESCAPED_DOMAIN_NAME (1005) bytes long. +// See definitions of MAX_ESCAPED_DOMAIN_LABEL and MAX_ESCAPED_DOMAIN_NAME for more detailed explanation. +extern char *ConvertDomainLabelToCString_withescape(const domainlabel *const name, char *cstr, char esc); +#define ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0) +#define ConvertDomainLabelToCString(D,C) ConvertDomainLabelToCString_withescape((D), (C), '\\') +extern char *ConvertDomainNameToCString_withescape(const domainname *const name, char *cstr, char esc); +#define ConvertDomainNameToCString_unescaped(D,C) ConvertDomainNameToCString_withescape((D), (C), 0) +#define ConvertDomainNameToCString(D,C) ConvertDomainNameToCString_withescape((D), (C), '\\') + +extern void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel); + +extern mDNSu8 *ConstructServiceName(domainname *const fqdn, const domainlabel *name, const domainname *type, const domainname *const domain); +extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain); + +// Note: Some old functions have been replaced by more sensibly-named versions. +// You can uncomment the hash-defines below if you don't want to have to change your source code right away. +// When updating your code, note that (unlike the old versions) *all* the new routines take the target object +// as their first parameter. +//#define ConvertCStringToDomainName(SRC,DST) MakeDomainNameFromDNSNameString((DST),(SRC)) +//#define ConvertCStringToDomainLabel(SRC,DST) MakeDomainLabelFromLiteralString((DST),(SRC)) +//#define AppendStringLabelToName(DST,SRC) AppendLiteralLabelString((DST),(SRC)) +//#define AppendStringNameToName(DST,SRC) AppendDNSNameString((DST),(SRC)) +//#define AppendDomainLabelToName(DST,SRC) AppendDomainLabel((DST),(SRC)) +//#define AppendDomainNameToName(DST,SRC) AppendDomainName((DST),(SRC)) + +// *************************************************************************** +#if 0 +#pragma mark - Other utility functions and macros +#endif + +// mDNS_vsnprintf/snprintf return the number of characters written, excluding the final terminating null. +// The output is always null-terminated: for example, if the output turns out to be exactly buflen long, +// then the output will be truncated by one character to allow space for the terminating null. +// Unlike standard C vsnprintf/snprintf, they return the number of characters *actually* written, +// not the number of characters that *would* have been printed were buflen unlimited. +extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg); +extern mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); +extern mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id); +extern char *DNSTypeName(mDNSu16 rrtype); +extern char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer); +#define RRDisplayString(m, rr) GetRRDisplayString_rdb(rr, &(rr)->rdata->u, (m)->MsgBuffer) +#define ARDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) +#define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) +extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); +extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); +extern mDNSBool IsPrivateV4Addr(mDNSAddr *addr); // returns true for RFC1918 private addresses + +#define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) +#define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3]) +#define mDNSSameEthAddress(A,B) ((A)->w[0] == (B)->w[0] && (A)->w[1] == (B)->w[1] && (A)->w[2] == (B)->w[2]) + +#define mDNSIPv4AddressIsZero(A) mDNSSameIPv4Address((A), zerov4Addr) +#define mDNSIPv6AddressIsZero(A) mDNSSameIPv6Address((A), zerov6Addr) + +#define mDNSIPv4AddressIsOnes(A) mDNSSameIPv4Address((A), onesIPv4Addr) +#define mDNSIPv6AddressIsOnes(A) mDNSSameIPv6Address((A), onesIPv6Addr) + +#define mDNSAddressIsAllDNSLinkGroup(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroupv4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) + +#define mDNSAddressIsZero(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsZero((X)->ip.v6)) ) + +#define mDNSAddressIsValidNonZero(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && !mDNSIPv4AddressIsZero((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && !mDNSIPv6AddressIsZero((X)->ip.v6)) ) + +#define mDNSAddressIsOnes(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsOnes((X)->ip.v6)) ) + +#define mDNSAddressIsValid(X) ( \ + ((X)->type == mDNSAddrType_IPv4) ? !(mDNSIPv4AddressIsZero((X)->ip.v4) || mDNSIPv4AddressIsOnes((X)->ip.v4)) : \ + ((X)->type == mDNSAddrType_IPv6) ? !(mDNSIPv6AddressIsZero((X)->ip.v6) || mDNSIPv6AddressIsOnes((X)->ip.v6)) : mDNSfalse) + + +// *************************************************************************** +#if 0 +#pragma mark - Authentication Support +#endif + +#define HMAC_LEN 64 +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + +// padded keys for inned/outer hash rounds +typedef struct + { + mDNSu8 ipad[HMAC_LEN]; + mDNSu8 opad[HMAC_LEN]; + } HMAC_Key; + +// Internal data structure to maintain authentication information for an update domain +typedef struct uDNS_AuthInfo + { + domainname zone; + domainname keyname; + HMAC_Key key; + struct uDNS_AuthInfo *next; + } uDNS_AuthInfo; + +// Unicast DNS and Dynamic Update specific Client Calls +// +// mDNS_SetSecretForZone tells the core to authenticate (via TSIG with an HMAC_MD5 hash of the shared secret) +// when dynamically updating a given zone (and its subdomains). The key used in authentication must be in +// domain name format. The shared secret must be a null-terminated base64 encoded string. A minimum size of +// 16 bytes (128 bits) is recommended for an MD5 hash as per RFC 2485. +// Calling this routine multiple times for a zone replaces previously entered values. Call with a NULL key +// to dissable authentication for the zone. + +extern mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const char *sharedSecret); + +// Hostname/Unicast Interface Configuration + +// All hostnames advertised point to one IPv4 address and/or one IPv6 address, set via SetPrimaryInterfaceInfo. Invoking this routine +// updates all existing hostnames to point to the new address. + +// A hostname is added via AddDynDNSHostName, which points to the primary interface's v4 and/or v6 addresss + +// The status callback is invoked to convey success or failure codes - the callback should not modify the AuthRecord or free memory. +// Added hostnames may be removed (deregistered) via mDNS_RemoveDynDNSHostName. + +// Host domains added prior to specification of the primary interface address and computer name will be deferred until +// these values are initialized. + +// When routable V4 interfaces are added or removed, mDNS_UpdateLLQs should be called to re-estabish LLQs in case the +// destination address for events (i.e. the route) has changed. For performance reasons, the caller is responsible for +// batching changes, e.g. calling the routine only once if multiple interfaces are simultanously removed or added. + +// DNS servers used to resolve unicast queries are specified by mDNS_AddDNSServer, and may later be removed via mDNS_DeleteDNSServers. +// For "split" DNS configurations, in which queries for different domains are sent to different servers (e.g. VPN and external), +// a domain may be associated with a DNS server. For standard configurations, specify the root label (".") or NULL. + +extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext); +extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn); +extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router); +extern void mDNS_UpdateLLQs(mDNS *m); +extern void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *dnsAddr, const domainname *domain); +extern void mDNS_DeleteDNSServers(mDNS *const m); + +// Routines called by the core, exported by DNSDigest.c + +// Convert a base64 encoded key into a binary byte stream +extern mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize); + +// Convert an arbitrary binary key (of any length) into an HMAC key (stored in AuthInfo struct) +extern void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, const mDNSu8 *key, mDNSu32 len); + +// sign a DNS message. The message must be compete, with all values in network byte order. end points to the end +// of the message, and is modified by this routine. numAdditionals is a pointer to the number of additional +// records in HOST byte order, which is incremented upon successful completion of this routine. The function returns +// the new end pointer on success, and NULL on failure. +extern mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info); + +// *************************************************************************** +#if 0 +#pragma mark - PlatformSupport interface +#endif + +// This section defines the interface to the Platform Support layer. +// Normal client code should not use any of types defined here, or directly call any of the functions defined here. +// The definitions are placed here because sometimes clients do use these calls indirectly, via other supported client operations. +// For example, AssignDomainName is a macro defined using mDNSPlatformMemCopy() + +// Every platform support module must provide the following functions. +// mDNSPlatformInit() typically opens a communication endpoint, and starts listening for mDNS packets. +// When Setup is complete, the platform support layer calls mDNSCoreInitComplete(). +// mDNSPlatformSendUDP() sends one UDP packet +// When a packet is received, the PlatformSupport code calls mDNSCoreReceive() +// mDNSPlatformClose() tidies up on exit +// +// Note: mDNSPlatformMemAllocate/mDNSPlatformMemFree are only required for handling oversized resource records and unicast DNS. +// If your target platform has a well-defined specialized application, and you know that all the records it uses +// are InlineCacheRDSize or less, then you can just make a simple mDNSPlatformMemAllocate() stub that always returns +// NULL. InlineCacheRDSize is a compile-time constant, which is set by default to 64. If you need to handle records +// a little larger than this and you don't want to have to implement run-time allocation and freeing, then you +// can raise the value of this constant to a suitable value (at the expense of increased memory usage). +// +// USE CAUTION WHEN CALLING mDNSPlatformRawTime: The m->timenow_adjust correction factor needs to be added +// Generally speaking: +// Code that's protected by the main mDNS lock should just use the m->timenow value +// Code outside the main mDNS lock should use mDNS_TimeNow(m) to get properly adjusted time +// In certain cases there may be reasons why it's necessary to get the time without taking the lock first +// (e.g. inside the routines that are doing the locking and unlocking, where a call to get the lock would result in a +// recursive loop); in these cases use mDNS_TimeNow_NoLock(m) to get mDNSPlatformRawTime with the proper correction factor added. +// +// mDNSPlatformUTC returns the time, in seconds, since Jan 1st 1970 UTC and is required for generating TSIG records + +extern mStatus mDNSPlatformInit (mDNS *const m); +extern void mDNSPlatformClose (mDNS *const m); +extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, +mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport); + +extern void mDNSPlatformLock (const mDNS *const m); +extern void mDNSPlatformUnlock (const mDNS *const m); + +extern void mDNSPlatformStrCopy (const void *src, void *dst); +extern mDNSu32 mDNSPlatformStrLen (const void *src); +extern void mDNSPlatformMemCopy (const void *src, void *dst, mDNSu32 len); +extern mDNSBool mDNSPlatformMemSame (const void *src, const void *dst, mDNSu32 len); +extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len); +extern void * mDNSPlatformMemAllocate (mDNSu32 len); +extern void mDNSPlatformMemFree (void *mem); +extern mDNSu32 mDNSPlatformRandomSeed (void); +extern mStatus mDNSPlatformTimeInit (void); +extern mDNSs32 mDNSPlatformRawTime (void); +extern mDNSs32 mDNSPlatformUTC (void); +#define mDNS_TimeNow_NoLock(m) (mDNSPlatformRawTime() + m->timenow_adjust) + +// Platform support modules should provide the following functions to map between opaque interface IDs +// and interface indexes in order to support the DNS-SD API. If your target platform does not support +// multiple interfaces and/or does not support the DNS-SD API, these functions can be empty. +extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 index); +extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id); + +// Every platform support module must provide the following functions if it is to support unicast DNS +// and Dynamic Update. +// All TCP socket operations implemented by the platform layer MUST NOT BLOCK. +// mDNSPlatformTCPConnect initiates a TCP connection with a peer, adding the socket descriptor to the +// main event loop. The return value indicates whether the connection succeeded, failed, or is pending +// (i.e. the call would block.) On return, the descriptor parameter is set to point to the connected socket. +// The TCPConnectionCallback is subsequently invoked when the connection +// completes (in which case the ConnectionEstablished parameter is true), or data is available for +// reading on the socket (indicated by the ConnectionEstablished parameter being false.) If the connection +// asynchronously fails, the TCPConnectionCallback should be invoked as usual, with the error being +// returned in subsequent calls to PlatformReadTCP or PlatformWriteTCP. (This allows for platforms +// with limited asynchronous error detection capabilities.) PlatformReadTCP and PlatformWriteTCP must +// return the number of bytes read/written, 0 if the call would block, and -1 if an error. +// PlatformTCPCloseConnection must close the connection to the peer and remove the descriptor from the +// event loop. CloseConnectin may be called at any time, including in a ConnectionCallback. + +typedef void (*TCPConnectionCallback)(int sd, void *context, mDNSBool ConnectionEstablished); +extern mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor); +extern void mDNSPlatformTCPCloseConnection(int sd); +extern int mDNSPlatformReadTCP(int sd, void *buf, int buflen); +extern int mDNSPlatformWriteTCP(int sd, const char *msg, int len); + +// Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain +// in browse/registration calls must implement these routines to get the "default" browse/registration list. +// The Get() functions must return a linked list of DNameListElem structs, allocated via mDNSPlatformMemAllocate. +// Platforms may implement the Get() calls via the mDNS_CopyDNameList() helper routine. +// Callers should free lists obtained via the Get() calls with th mDNS_FreeDNameList routine, provided by the core. + +typedef struct DNameListElem + { + domainname name; + struct DNameListElem *next; + } DNameListElem; + +extern DNameListElem *mDNSPlatformGetSearchDomainList(void); +extern DNameListElem *mDNSPlatformGetRegDomainList(void); + +// Helper functions provided by the core +extern DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig); +extern void mDNS_FreeDNameList(DNameListElem *list); + +#ifdef _LEGACY_NAT_TRAVERSAL_ +// Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core. + +#define DYN_PORT_MIN 49152 // ephemeral port range +#define DYN_PORT_MAX 65535 +#define LEGACY_NATMAP_MAX_TRIES 4 // if our desired mapping is taken, how many times we try mapping to a random port + +extern mStatus LNT_GetPublicIP(mDNSOpaque32 *ip); +extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp); +extern mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp); +#endif // _LEGACY_NAT_TRAVERSAL_ + +// The core mDNS code provides these functions, for the platform support code to call at appropriate times +// +// mDNS_SetFQDN() is called once on startup (typically from mDNSPlatformInit()) +// and then again on each subsequent change of the host name. +// +// mDNS_RegisterInterface() is used by the platform support layer to inform mDNSCore of what +// physical and/or logical interfaces are available for sending and receiving packets. +// Typically it is called on startup for each available interface, but register/deregister may be +// called again later, on multiple occasions, to inform the core of interface configuration changes. +// If set->Advertise is set non-zero, then mDNS_RegisterInterface() also registers the standard +// resource records that should be associated with every publicised IP address/interface: +// -- Name-to-address records (A/AAAA) +// -- Address-to-name records (PTR) +// -- Host information (HINFO) +// IMPORTANT: The specified mDNSInterfaceID MUST NOT be 0, -1, or -2; these values have special meaning +// mDNS_RegisterInterface does not result in the registration of global hostnames via dynamic update - +// see mDNS_SetPrimaryInterfaceInfo, mDNS_AddDynDNSHostName, etc. for this purpose. +// Note that the set may be deallocated immediately after it is deregistered via mDNS_DeegisterInterface. +// +// mDNS_RegisterDNS() is used by the platform support layer to provide the core with the addresses of +// available domain name servers for unicast queries/updates. RegisterDNS() should be called once for +// each name server, typically at startup, or when a new name server becomes available. DeregiterDNS() +// must be called whenever a registered name server becomes unavailable. DeregisterDNSList deregisters +// all registered servers. mDNS_DNSRegistered() returns true if one or more servers are registered in the core. +// +// mDNSCoreInitComplete() is called when the platform support layer is finished. +// Typically this is at the end of mDNSPlatformInit(), but may be later +// (on platforms like OT that allow asynchronous initialization of the networking stack). +// +// mDNSCoreReceive() is called when a UDP packet is received +// +// mDNSCoreMachineSleep() is called when the machine sleeps or wakes +// (This refers to heavyweight laptop-style sleep/wake that disables network access, +// not lightweight second-by-second CPU power management modes.) + +extern void mDNS_SetFQDN(mDNS *const m); +extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping); +extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping); +extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); +extern void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, + const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); +extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); + +extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); + +// *************************************************************************** +#if 0 +#pragma mark - Compile-Time assertion checks +#endif + +// Some C compiler cleverness. We can make the compiler check certain things for +// us, and report compile-time errors if anything is wrong. The usual way to do +// this would be to use a run-time "if" statement, but then you don't find out +// what's wrong until you run the software. This way, if the assertion condition +// is false, the array size is negative, and the complier complains immediately. + +struct mDNS_CompileTimeAssertionChecks + { + // Check that the compiler generated our on-the-wire packet format structure definitions + // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. + char assert0[(sizeof(rdataSRV) == 262 ) ? 1 : -1]; + char assert1[(sizeof(DNSMessageHeader) == 12 ) ? 1 : -1]; + char assert2[(sizeof(DNSMessage) == 12+AbsoluteMaxDNSMessageData) ? 1 : -1]; + char assert3[(sizeof(mDNSs8) == 1 ) ? 1 : -1]; + char assert4[(sizeof(mDNSu8) == 1 ) ? 1 : -1]; + char assert5[(sizeof(mDNSs16) == 2 ) ? 1 : -1]; + char assert6[(sizeof(mDNSu16) == 2 ) ? 1 : -1]; + char assert7[(sizeof(mDNSs32) == 4 ) ? 1 : -1]; + char assert8[(sizeof(mDNSu32) == 4 ) ? 1 : -1]; + char assert9[(sizeof(mDNSOpaque16) == 2 ) ? 1 : -1]; + char assertA[(sizeof(mDNSOpaque32) == 4 ) ? 1 : -1]; + char assertB[(sizeof(mDNSOpaque128) == 16 ) ? 1 : -1]; + char assertC[(sizeof(CacheRecord ) >= sizeof(CacheGroup) ) ? 1 : -1]; + char assertD[(sizeof(int) >= 4 ) ? 1 : -1]; + }; + +// *************************************************************************** + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c new file mode 100644 index 0000000000..179d84027f --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c @@ -0,0 +1,1771 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Formatting notes: + * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion + * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>, + * but for the sake of brevity here I will say just this: Curly braces are not syntactially + * part of an "if" statement; they are the beginning and ending markers of a compound statement; + * therefore common sense dictates that if they are part of a compound statement then they + * should be indented to the same level as everything else in that compound statement. + * Indenting curly braces at the same level as the "if" implies that curly braces are + * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" + * thinking that variables x and y are both of type "char*" -- and anyone who doesn't + * understand why variable y is not of type "char*" just proves the point that poor code + * layout leads people to unfortunate misunderstandings about how the C language really works.) + + Change History (most recent first): + +$Log: mDNSPosix.c,v $ +Revision 1.78.2.1 2006/08/29 06:24:34 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.78 2006/06/28 09:12:22 cheshire +Added debugging message + +Revision 1.77 2006/03/19 02:00:11 cheshire +<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions + +Revision 1.76 2006/01/09 19:29:16 cheshire +<rdar://problem/4403128> Cap number of "sendto failed" messages we allow mDNSResponder to log + +Revision 1.75 2006/01/05 22:04:57 cheshire +<rdar://problem/4399479> Log error message when send fails with "operation not permitted" + +Revision 1.74 2006/01/05 21:45:27 cheshire +<rdar://problem/4400118> Fix uninitialized structure member in IPv6 code + +Revision 1.73 2005/10/11 21:31:46 cheshire +<rdar://problem/4296177> Don't depend on IP_RECVTTL succeeding (not available on all platforms) + +Revision 1.72 2005/09/08 20:45:26 cheshire +Default dot-local host name should be "Computer" not "Macintosh", +since the machine this is running on is most likely NOT a Mac. + +Revision 1.71 2005/02/26 01:29:12 cheshire +Ignore multicasts accidentally delivered to our unicast receiving socket + +Revision 1.70 2005/02/04 00:39:59 cheshire +Move ParseDNSServers() from PosixDaemon.c to mDNSPosix.c so all Posix client layers can use it + +Revision 1.69 2004/12/18 02:03:28 cheshire +Need to #include "dns_sd.h" + +Revision 1.68 2004/12/18 00:51:52 cheshire +Use symbolic constant kDNSServiceInterfaceIndexLocalOnly instead of (mDNSu32) ~0 + +Revision 1.67 2004/12/17 23:37:48 cheshire +<rdar://problem/3485365> Guard against repeating wireless dissociation/re-association +(and other repetitive configuration changes) + +Revision 1.66 2004/12/01 04:27:28 cheshire +<rdar://problem/3872803> Darwin patches for Solaris and Suse +Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc. + +Revision 1.65 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.64 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.63 2004/11/12 03:16:43 rpantos +rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName + +Revision 1.62 2004/10/28 03:24:42 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.61 2004/10/16 00:17:01 cheshire +<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check + +Revision 1.60 2004/09/26 23:20:36 ksekar +<rdar://problem/3813108> Allow default registrations in multiple wide-area domains + +Revision 1.59 2004/09/21 21:02:55 cheshire +Set up ifname before calling mDNS_RegisterInterface() + +Revision 1.58 2004/09/17 01:08:54 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.57 2004/09/17 00:19:11 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.56 2004/09/17 00:15:56 cheshire +Rename mDNSPlatformInit_ReceiveUnicast to mDNSPlatformInit_CanReceiveUnicast + +Revision 1.55 2004/09/16 00:24:49 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.54 2004/09/15 23:55:00 ksekar +<rdar://problem/3800597> mDNSPosix should #include stdint.h + +Revision 1.53 2004/09/14 23:42:36 cheshire +<rdar://problem/3801296> Need to seed random number generator from platform-layer data + +Revision 1.52 2004/08/25 16:42:13 ksekar +Fix Posix build - change mDNS_SetFQDNs to mDNS_SetFQDN, remove unicast +hostname parameter. + +Revision 1.51 2004/08/14 03:22:42 cheshire +<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.50 2004/08/11 01:20:20 cheshire +Declare private local functions using "mDNSlocal" + +Revision 1.49 2004/07/26 22:49:31 ksekar +<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client + +Revision 1.48 2004/07/20 01:47:36 rpantos +NOT_HAVE_SA_LEN applies to v6, too. And use more-portable s6_addr. + +Revision 1.47 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.46 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.45 2004/05/12 22:03:09 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSEmbeddedAPI.h), impelemted to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.44 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.43 2004/04/14 23:09:29 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.42 2004/04/09 17:43:04 cheshire +Make sure to set the McastTxRx field so that duplicate suppression works correctly + +Revision 1.41 2004/02/06 01:19:51 cheshire +Conditionally exclude IPv6 code unless HAVE_IPV6 is set + +Revision 1.40 2004/02/05 01:00:01 rpantos +Fix some issues that turned up when building for FreeBSD. + +Revision 1.39 2004/01/28 21:12:15 cheshire +Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6) + +Revision 1.38 2004/01/27 20:15:23 cheshire +<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 + +Revision 1.37 2004/01/24 05:12:03 cheshire +<rdar://problem/3534352>: Need separate socket for issuing unicast queries + +Revision 1.36 2004/01/24 04:59:16 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.35 2004/01/23 21:37:08 cheshire +For consistency, rename multicastSocket to multicastSocket4, and multicastSocketv6 to multicastSocket6 + +Revision 1.34 2004/01/22 03:43:09 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.33 2004/01/21 21:54:20 cheshire +<rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.32 2004/01/20 01:49:28 rpantos +Tweak error handling of last checkin a bit. + +Revision 1.31 2004/01/20 01:39:27 rpantos +Respond to If changes by rebuilding interface list. + +Revision 1.30 2003/12/11 19:40:36 cheshire +Fix 'destAddr.type == senderAddr.type;' that should have said 'destAddr.type = senderAddr.type;' + +Revision 1.29 2003/12/11 18:53:22 cheshire +Fix compiler warning reported by Paul Guyot + +Revision 1.28 2003/12/11 03:03:51 rpantos +Clean up mDNSPosix so that it builds on OS X again. + +Revision 1.27 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + +Revision 1.26 2003/11/14 20:59:09 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.25 2003/10/30 19:25:49 cheshire +Fix signed/unsigned warning on certain compilers + +Revision 1.24 2003/08/18 23:12:23 cheshire +<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime() + +Revision 1.23 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.22 2003/08/06 18:46:15 cheshire +LogMsg() errors are serious -- always report them to stderr, regardless of debugging level + +Revision 1.21 2003/08/06 18:20:51 cheshire +Makefile cleanup + +Revision 1.20 2003/08/05 23:56:26 cheshire +Update code to compile with the new mDNSCoreReceive() function that requires a TTL +(Right now mDNSPosix.c just reports 255 -- we should fix this) + +Revision 1.19 2003/07/19 03:15:16 cheshire +Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, +and add the obvious trivial implementations to each platform support layer + +Revision 1.18 2003/07/14 18:11:54 cheshire +Fix stricter compiler warnings + +Revision 1.17 2003/07/13 01:08:38 cheshire +There's not much point running mDNS over a point-to-point link; exclude those + +Revision 1.16 2003/07/02 21:19:59 cheshire +<rdar://problem/3313413> Update copyright notices, etc., in source code comments + +Revision 1.15 2003/06/18 05:48:41 cheshire +Fix warnings + +Revision 1.14 2003/05/26 03:21:30 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.13 2003/05/26 03:01:28 cheshire +<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.12 2003/05/21 03:49:18 cheshire +Fix warning + +Revision 1.11 2003/05/06 00:00:50 cheshire +<rdar://problem/3248914> Rationalize naming of domainname manipulation functions + +Revision 1.10 2003/04/25 01:45:57 cheshire +<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.9 2003/03/20 21:10:31 cheshire +Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris + +Revision 1.8 2003/03/15 04:40:38 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.7 2003/03/13 03:46:21 cheshire +Fixes to make the code build on Linux + +Revision 1.6 2003/03/08 00:35:56 cheshire +Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.5 2002/12/23 22:13:31 jgraessl +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.4 2002/09/27 01:47:45 cheshire +Workaround for Linux 2.0 systems that don't have IP_PKTINFO + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 21:25:36 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.1 2002/09/17 06:24:34 cheshire +First checkin +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "dns_sd.h" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <stdarg.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/select.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <time.h> // platform support for UTC time + +#if USES_NETLINK +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#else // USES_NETLINK +#include <net/route.h> +#include <net/if.h> +#endif // USES_NETLINK + +#include "mDNSUNP.h" +#include "GenLinkedList.h" + +// *************************************************************************** +// Structures + +// We keep a list of client-supplied event sources in PosixEventSource records +struct PosixEventSource + { + mDNSPosixEventCallback Callback; + void *Context; + int fd; + struct PosixEventSource *Next; + }; +typedef struct PosixEventSource PosixEventSource; + +// Context record for interface change callback +struct IfChangeRec + { + int NotifySD; + mDNS* mDNS; + }; +typedef struct IfChangeRec IfChangeRec; + +// Note that static data is initialized to zero in (modern) C. +static fd_set gEventFDs; +static int gMaxFD; // largest fd in gEventFDs +static GenLinkedList gEventSources; // linked list of PosixEventSource's +static sigset_t gEventSignalSet; // Signals which event loop listens for +static sigset_t gEventSignals; // Signals which were received while inside loop + +// *************************************************************************** +// Globals (for debugging) + +static int num_registered_interfaces = 0; +static int num_pkts_accepted = 0; +static int num_pkts_rejected = 0; + +// *************************************************************************** +// Functions + +int gMDNSPlatformPosixVerboseLevel = 0; + +#define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr) + +mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort) + { + switch (sa->sa_family) + { + case AF_INET: + { + struct sockaddr_in* sin = (struct sockaddr_in*)sa; + ipAddr->type = mDNSAddrType_IPv4; + ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr; + if (ipPort) ipPort->NotAnInteger = sin->sin_port; + break; + } + +#if HAVE_IPV6 + case AF_INET6: + { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; +#ifndef NOT_HAVE_SA_LEN + assert(sin6->sin6_len == sizeof(*sin6)); +#endif + ipAddr->type = mDNSAddrType_IPv6; + ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + if (ipPort) ipPort->NotAnInteger = sin6->sin6_port; + break; + } +#endif + + default: + verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family); + ipAddr->type = mDNSAddrType_None; + if (ipPort) ipPort->NotAnInteger = 0; + break; + } + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Send and Receive +#endif + +// mDNS core calls this routine when it needs to send a packet. +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) + { + int err = 0; + struct sockaddr_storage to; + PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID); + int sendingsocket = -1; + + assert(m != NULL); + assert(msg != NULL); + assert(end != NULL); + assert( (((char *) end) - ((char *) msg)) > 0 ); + assert(dstPort.NotAnInteger != 0); + + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin = (struct sockaddr_in*)&to; +#ifndef NOT_HAVE_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + sin->sin_family = AF_INET; + sin->sin_port = dstPort.NotAnInteger; + sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + sendingsocket = thisIntf ? thisIntf->multicastSocket4 : m->p->unicastSocket4; + } + +#if HAVE_IPV6 + else if (dst->type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; + mDNSPlatformMemZero(sin6, sizeof(*sin6)); +#ifndef NOT_HAVE_SA_LEN + sin6->sin6_len = sizeof(*sin6); +#endif + sin6->sin6_family = AF_INET6; + sin6->sin6_port = dstPort.NotAnInteger; + sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; + sendingsocket = thisIntf ? thisIntf->multicastSocket6 : m->p->unicastSocket6; + } +#endif + + if (sendingsocket >= 0) + err = sendto(sendingsocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); + + if (err > 0) err = 0; + else if (err < 0) + { + static int MessageCount = 0; + // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations + if (!mDNSAddressIsAllDNSLinkGroup(dst)) + if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); + + if (MessageCount < 1000) + { + MessageCount++; + if (thisIntf) + LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d", + errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index); + else + LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst); + } + } + + return PosixErrorToStatus(err); + } + +// This routine is called when the main loop detects that data is available on a socket. +mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) + { + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort; + ssize_t packetLen; + DNSMessage packet; + struct my_in_pktinfo packetInfo; + struct sockaddr_storage from; + socklen_t fromLen; + int flags; + mDNSu8 ttl; + mDNSBool reject; + const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL; + + assert(m != NULL); + assert(skt >= 0); + + fromLen = sizeof(from); + flags = 0; + packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl); + + if (packetLen >= 0) + { + SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); + SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); + + // If we have broken IP_RECVDSTADDR functionality (so far + // I've only seen this on OpenBSD) then apply a hack to + // convince mDNS Core that this isn't a spoof packet. + // Basically what we do is check to see whether the + // packet arrived as a multicast and, if so, set its + // destAddr to the mDNS address. + // + // I must admit that I could just be doing something + // wrong on OpenBSD and hence triggering this problem + // but I'm at a loss as to how. + // + // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have + // no way to tell the destination address or interface this packet arrived on, + // so all we can do is just assume it's a multicast + + #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) + if ( (destAddr.NotAnInteger == 0) && (flags & MSG_MCAST) ) + { + destAddr.type = senderAddr.type; + if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroupv4; + else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroupv6; + } + #endif + + // We only accept the packet if the interface on which it came + // in matches the interface associated with this socket. + // We do this match by name or by index, depending on which + // information is available. recvfrom_flags sets the name + // to "" if the name isn't available, or the index to -1 + // if the index is available. This accomodates the various + // different capabilities of our target platforms. + + reject = mDNSfalse; + if (!intf) + { + // Ignore multicasts accidentally delivered to our unicast receiving socket + if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1; + } + else + { + if ( packetInfo.ipi_ifname[0] != 0 ) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0); + else if ( packetInfo.ipi_ifindex != -1 ) reject = (packetInfo.ipi_ifindex != intf->index); + + if (reject) + { + verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d", + &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, + &intf->coreIntf.ip, intf->intfName, intf->index, skt); + packetLen = -1; + num_pkts_rejected++; + if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2) + { + fprintf(stderr, + "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n", + num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected); + num_pkts_accepted = 0; + num_pkts_rejected = 0; + } + } + else + { + verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d", + &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt); + num_pkts_accepted++; + } + } + } + + if (packetLen >= 0) + mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, + &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); + } + +mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor) + { + (void)dst; // Unused + (void)dstport; // Unused + (void)InterfaceID; // Unused + (void)callback; // Unused + (void)context; // Unused + (void)descriptor; // Unused + return(mStatus_UnsupportedErr); + } + +mDNSexport void mDNSPlatformTCPCloseConnection(int sd) + { + (void)sd; // Unused + } + +mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) + { + (void)sd; // Unused + (void)buf; // Unused + (void)buflen; // Unused + return(0); + } + +mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) + { + (void)sd; // Unused + (void)msg; // Unused + (void)len; // Unused + return(0); + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Get/Free Search Domain List +#endif + +mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) + { + static DNameListElem tmp; + static mDNSBool init = mDNSfalse; + + if (!init) + { + MakeDomainNameFromDNSNameString(&tmp.name, "local."); + tmp.next = NULL; + init = mDNStrue; + } + return mDNS_CopyDNameList(&tmp); + } + +mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void) + { + return NULL; + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Init and Term +#endif + +// This gets the current hostname, truncating it at the first dot if necessary +mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) + { + int len = 0; + gethostname((char *)(&namelabel->c[1]), MAX_DOMAIN_LABEL); + while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++; + namelabel->c[0] = len; + } + +// On OS X this gets the text of the field labelled "Computer Name" in the Sharing Prefs Control Panel +// Other platforms can either get the information from the appropriate place, +// or they can alternatively just require all registering services to provide an explicit name +mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) + { + // On Unix we have no better name than the host name, so we just use that. + GetUserSpecifiedRFC1034ComputerName( namelabel); + } + +mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) + { + char line[256]; + char nameserver[16]; + char keyword[10]; + int numOfServers = 0; + FILE *fp = fopen(filePath, "r"); + if (fp == NULL) return -1; + while (fgets(line,sizeof(line),fp)) + { + struct in_addr ina; + line[255]='\0'; // just to be safe + if (sscanf(line,"%10s %15s", keyword, nameserver) != 2) continue; // it will skip whitespaces + if (strncmp(keyword,"nameserver",10)) continue; + if (inet_aton(nameserver, (struct in_addr *)&ina) != 0) + { + mDNSAddr DNSAddr; + DNSAddr.type = mDNSAddrType_IPv4; + DNSAddr.ip.v4.NotAnInteger = ina.s_addr; + mDNS_AddDNSServer(m, &DNSAddr, NULL); + numOfServers++; + } + } + return (numOfServers > 0) ? 0 : -1; + } + +// Searches the interface list looking for the named interface. +// Returns a pointer to if it found, or NULL otherwise. +mDNSlocal PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char *intfName) + { + PosixNetworkInterface *intf; + + assert(m != NULL); + assert(intfName != NULL); + + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ( (intf != NULL) && (strcmp(intf->intfName, intfName) != 0) ) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); + + return intf; + } + +mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 index) + { + PosixNetworkInterface *intf; + + assert(m != NULL); + + if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); + + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ( (intf != NULL) && (mDNSu32) intf->index != index) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); + + return (mDNSInterfaceID) intf; + } + +mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id) + { + PosixNetworkInterface *intf; + + assert(m != NULL); + + if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); + + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ( (intf != NULL) && (mDNSInterfaceID) intf != id) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); + + return intf ? intf->index : 0; + } + +// Frees the specified PosixNetworkInterface structure. The underlying +// interface must have already been deregistered with the mDNS core. +mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf) + { + assert(intf != NULL); + if (intf->intfName != NULL) free((void *)intf->intfName); + if (intf->multicastSocket4 != -1) assert(close(intf->multicastSocket4) == 0); +#if HAVE_IPV6 + if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0); +#endif + free(intf); + } + +// Grab the first interface, deregister it, free it, and repeat until done. +mDNSlocal void ClearInterfaceList(mDNS *const m) + { + assert(m != NULL); + + while (m->HostInterfaces) + { + PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces); + mDNS_DeregisterInterface(m, &intf->coreIntf, mDNSfalse); + if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName); + FreePosixNetworkInterface(intf); + } + num_registered_interfaces = 0; + num_pkts_accepted = 0; + num_pkts_rejected = 0; + } + +// Sets up a send/receive socket. +// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface +// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries +mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr) + { + int err = 0; + static const int kOn = 1; + static const int kIntTwoFiveFive = 255; + static const unsigned char kByteTwoFiveFive = 255; + const mDNSBool JoinMulticastGroup = (port.NotAnInteger != 0); + + (void) interfaceIndex; // This parameter unused on plaforms that don't have IPv6 + assert(intfAddr != NULL); + assert(sktPtr != NULL); + assert(*sktPtr == -1); + + // Open the socket... + if (intfAddr->sa_family == AF_INET ) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +#if HAVE_IPV6 + else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); +#endif + else return EINVAL; + + if (*sktPtr < 0) { err = errno; perror("socket"); } + + // ... with a shared UDP port, if it's for multicast receiving + if (err == 0 && port.NotAnInteger) + { + #if defined(SO_REUSEPORT) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); + #elif defined(SO_REUSEADDR) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + #else + #error This platform has no way to avoid address busy errors on multicast. + #endif + if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } + } + + // We want to receive destination addresses and interface identifiers. + if (intfAddr->sa_family == AF_INET) + { + struct ip_mreq imr; + struct sockaddr_in bindAddr; + if (err == 0) + { + #if defined(IP_PKTINFO) // Linux and Solaris + err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } + #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD + #if defined(IP_RECVDSTADDR) + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } + #endif + #if defined(IP_RECVIF) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } + } + #endif + #else + #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts + #endif + } + #if defined(IP_RECVTTL) // Linux + if (err == 0) + { + setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn)); + // We no longer depend on being able to get the received TTL, so don't worry if the option fails + } + #endif + + // Add multicast group membership on this interface + if (err == 0 && JoinMulticastGroup) + { + imr.imr_multiaddr.s_addr = AllDNSLinkGroupv4.NotAnInteger; + imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; + err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); } + } + + // Specify outgoing interface too + if (err == 0 && JoinMulticastGroup) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr)); + if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); } + } + + // Per the mDNS spec, send unicast packets with TTL 255 + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); } + } + + // and multicast packets with TTL 255 too + // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); + if (err < 0 && errno == EINVAL) + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } + } + + // And start listening for packets + if (err == 0) + { + bindAddr.sin_family = AF_INET; + bindAddr.sin_port = port.NotAnInteger; + bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket + err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); + if (err < 0) { err = errno; perror("bind"); fflush(stderr); } + } + } // endif (intfAddr->sa_family == AF_INET) + +#if HAVE_IPV6 + else if (intfAddr->sa_family == AF_INET6) + { + struct ipv6_mreq imr6; + struct sockaddr_in6 bindAddr6; + #if defined(IPV6_RECVPKTINFO) // Solaris + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_RECVPKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } + } + + #elif defined(IPV6_PKTINFO) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_PKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } + } + #else + #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts + #endif + #if defined(IPV6_RECVHOPLIMIT) // Solaris + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); } + } + + #elif defined(IPV6_HOPLIMIT) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_HOPLIMIT, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); } + } + #endif + + // Add multicast group membership on this interface + if (err == 0 && JoinMulticastGroup) + { + imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroupv6; + imr6.ipv6mr_interface = interfaceIndex; + //LogMsg("Joining %.16a on %d", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6)); + if (err < 0) + { + err = errno; + verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); + perror("setsockopt - IPV6_JOIN_GROUP"); + } + } + + // Specify outgoing interface too + if (err == 0 && JoinMulticastGroup) + { + u_int multicast_if = interfaceIndex; + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); } + } + + // We want to receive only IPv6 packets on this socket. + // Without this option, we may get IPv4 addresses as mapped addresses. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); } + } + + // Per the mDNS spec, send unicast packets with TTL 255 + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); } + } + + // and multicast packets with TTL 255 too + // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); + if (err < 0 && errno == EINVAL) + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } + } + + // And start listening for packets + if (err == 0) + { + mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6)); +#ifndef NOT_HAVE_SA_LEN + bindAddr6.sin6_len = sizeof(bindAddr6); +#endif + bindAddr6.sin6_family = AF_INET6; + bindAddr6.sin6_port = port.NotAnInteger; + bindAddr6.sin6_flowinfo = 0; + bindAddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket + bindAddr6.sin6_scope_id = 0; + err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6)); + if (err < 0) { err = errno; perror("bind"); fflush(stderr); } + } + } // endif (intfAddr->sa_family == AF_INET6) +#endif + + // Set the socket to non-blocking. + if (err == 0) + { + err = fcntl(*sktPtr, F_GETFL, 0); + if (err < 0) err = errno; + else + { + err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK); + if (err < 0) err = errno; + } + } + + // Clean up + if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; } + assert( (err == 0) == (*sktPtr != -1) ); + return err; + } + +// Creates a PosixNetworkInterface for the interface whose IP address is +// intfAddr and whose name is intfName and registers it with mDNS core. +mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct sockaddr *intfMask, const char *intfName, int intfIndex) + { + int err = 0; + PosixNetworkInterface *intf; + PosixNetworkInterface *alias = NULL; + + assert(m != NULL); + assert(intfAddr != NULL); + assert(intfName != NULL); + assert(intfMask != NULL); + + // Allocate the interface structure itself. + intf = (PosixNetworkInterface*)malloc(sizeof(*intf)); + if (intf == NULL) { assert(0); err = ENOMEM; } + + // And make a copy of the intfName. + if (err == 0) + { + intf->intfName = strdup(intfName); + if (intf->intfName == NULL) { assert(0); err = ENOMEM; } + } + + if (err == 0) + { + // Set up the fields required by the mDNS core. + SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL); + SockAddrTomDNSAddr(intfMask, &intf->coreIntf.mask, NULL); + //LogMsg("SetupOneInterface: %#a %#a", &intf->coreIntf.ip, &intf->coreIntf.mask); + strncpy(intf->coreIntf.ifname, intfName, sizeof(intf->coreIntf.ifname)); + intf->coreIntf.ifname[sizeof(intf->coreIntf.ifname)-1] = 0; + intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; + intf->coreIntf.McastTxRx = mDNStrue; + + // Set up the extra fields in PosixNetworkInterface. + assert(intf->intfName != NULL); // intf->intfName already set up above + intf->index = intfIndex; + intf->multicastSocket4 = -1; +#if HAVE_IPV6 + intf->multicastSocket6 = -1; +#endif + alias = SearchForInterfaceByName(m, intf->intfName); + if (alias == NULL) alias = intf; + intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias; + + if (alias != intf) + debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip); + } + + // Set up the multicast socket + if (err == 0) + { + if (alias->multicastSocket4 == -1 && intfAddr->sa_family == AF_INET) + err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket4); +#if HAVE_IPV6 + else if (alias->multicastSocket6 == -1 && intfAddr->sa_family == AF_INET6) + err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket6); +#endif + } + + // The interface is all ready to go, let's register it with the mDNS core. + if (err == 0) + err = mDNS_RegisterInterface(m, &intf->coreIntf, mDNSfalse); + + // Clean up. + if (err == 0) + { + num_registered_interfaces++; + debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip); + if (gMDNSPlatformPosixVerboseLevel > 0) + fprintf(stderr, "Registered interface %s\n", intf->intfName); + } + else + { + // Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL. + debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err); + if (intf) { FreePosixNetworkInterface(intf); intf = NULL; } + } + + assert( (err == 0) == (intf != NULL) ); + + return err; + } + +// Call get_ifi_info() to obtain a list of active interfaces and call SetupOneInterface() on each one. +mDNSlocal int SetupInterfaceList(mDNS *const m) + { + mDNSBool foundav4 = mDNSfalse; + int err = 0; + struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue); + struct ifi_info *firstLoopback = NULL; + + assert(m != NULL); + debugf("SetupInterfaceList"); + + if (intfList == NULL) err = ENOENT; + +#if HAVE_IPV6 + if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */ + { + struct ifi_info **p = &intfList; + while (*p) p = &(*p)->ifi_next; + *p = get_ifi_info(AF_INET6, mDNStrue); + } +#endif + + if (err == 0) + { + struct ifi_info *i = intfList; + while (i) + { + if ( ((i->ifi_addr->sa_family == AF_INET) +#if HAVE_IPV6 + || (i->ifi_addr->sa_family == AF_INET6) +#endif + ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT) ) + { + if (i->ifi_flags & IFF_LOOPBACK) + { + if (firstLoopback == NULL) + firstLoopback = i; + } + else + { + if (SetupOneInterface(m, i->ifi_addr, i->ifi_netmask, i->ifi_name, i->ifi_index) == 0) + if (i->ifi_addr->sa_family == AF_INET) + foundav4 = mDNStrue; + } + } + i = i->ifi_next; + } + + // If we found no normal interfaces but we did find a loopback interface, register the + // loopback interface. This allows self-discovery if no interfaces are configured. + // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. + // In the interim, we skip loopback interface only if we found at least one v4 interface to use + // if ( (m->HostInterfaces == NULL) && (firstLoopback != NULL) ) + if ( !foundav4 && firstLoopback ) + (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_netmask, firstLoopback->ifi_name, firstLoopback->ifi_index); + } + + // Clean up. + if (intfList != NULL) free_ifi_info(intfList); + return err; + } + +#if USES_NETLINK + +// See <http://www.faqs.org/rfcs/rfc3549.html> for a description of NetLink + +// Open a socket that will receive interface change notifications +mDNSlocal mStatus OpenIfNotifySocket( int *pFD) + { + mStatus err = mStatus_NoError; + struct sockaddr_nl snl; + int sock; + int ret; + + sock = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + return errno; + + // Configure read to be non-blocking because inbound msg size is not known in advance + (void) fcntl( sock, F_SETFL, O_NONBLOCK); + + /* Subscribe the socket to Link & IP addr notifications. */ + bzero( &snl, sizeof snl); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; + ret = bind( sock, (struct sockaddr *) &snl, sizeof snl); + if ( 0 == ret) + *pFD = sock; + else + err = errno; + + return err; + } + +#if MDNS_DEBUGMSGS +mDNSlocal void PrintNetLinkMsg( const struct nlmsghdr *pNLMsg) + { + const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" }; + const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" }; + + printf( "nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len, + pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[ pNLMsg->nlmsg_type] : kNLRtMsgTypes[ pNLMsg->nlmsg_type - RTM_BASE], + pNLMsg->nlmsg_flags); + + if ( RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK) + { + struct ifinfomsg *pIfInfo = (struct ifinfomsg*) NLMSG_DATA( pNLMsg); + printf( "ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family, + pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change); + + } + else if ( RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR) + { + struct ifaddrmsg *pIfAddr = (struct ifaddrmsg*) NLMSG_DATA( pNLMsg); + printf( "ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family, + pIfAddr->ifa_index, pIfAddr->ifa_flags); + } + printf( "\n"); + } +#endif + +mDNSlocal mDNSu32 ProcessRoutingNotification( int sd) +// Read through the messages on sd and if any indicate that any interface records should +// be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. + { + ssize_t readCount; + char buff[ 4096]; + struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff; + mDNSu32 result = 0; + + // The structure here is more complex than it really ought to be because, + // unfortunately, there's no good way to size a buffer in advance large + // enough to hold all pending data and so avoid message fragmentation. + // (Note that FIONREAD is not supported on AF_NETLINK.) + + readCount = read( sd, buff, sizeof buff); + while ( 1) + { + // Make sure we've got an entire nlmsghdr in the buffer, and payload, too. + // If not, discard already-processed messages in buffer and read more data. + if ( ( (char*) &pNLMsg[1] > ( buff + readCount)) || // i.e. *pNLMsg extends off end of buffer + ( (char*) pNLMsg + pNLMsg->nlmsg_len > ( buff + readCount))) + { + if ( buff < (char*) pNLMsg) // we have space to shuffle + { + // discard processed data + readCount -= ( (char*) pNLMsg - buff); + memmove( buff, pNLMsg, readCount); + pNLMsg = (struct nlmsghdr*) buff; + + // read more data + readCount += read( sd, buff + readCount, sizeof buff - readCount); + continue; // spin around and revalidate with new readCount + } + else + break; // Otherwise message does not fit in buffer + } + +#if MDNS_DEBUGMSGS + PrintNetLinkMsg( pNLMsg); +#endif + + // Process the NetLink message + if ( pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK) + result |= 1 << ((struct ifinfomsg*) NLMSG_DATA( pNLMsg))->ifi_index; + else if ( pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR) + result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA( pNLMsg))->ifa_index; + + // Advance pNLMsg to the next message in the buffer + if ( ( pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE) + { + ssize_t len = readCount - ( (char*)pNLMsg - buff); + pNLMsg = NLMSG_NEXT( pNLMsg, len); + } + else + break; // all done! + } + + return result; + } + +#else // USES_NETLINK + +// Open a socket that will receive interface change notifications +mDNSlocal mStatus OpenIfNotifySocket( int *pFD) + { + *pFD = socket( AF_ROUTE, SOCK_RAW, 0); + + if ( *pFD < 0) + return mStatus_UnknownErr; + + // Configure read to be non-blocking because inbound msg size is not known in advance + (void) fcntl( *pFD, F_SETFL, O_NONBLOCK); + + return mStatus_NoError; + } + +#if MDNS_DEBUGMSGS +mDNSlocal void PrintRoutingSocketMsg( const struct ifa_msghdr *pRSMsg) + { + const char *kRSMsgTypes[] = { "", "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING", + "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", + "RTM_NEWADDR", "RTM_DELADDR", "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" }; + + int index = pRSMsg->ifam_type == RTM_IFINFO ? ((struct if_msghdr*) pRSMsg)->ifm_index : pRSMsg->ifam_index; + + printf( "ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[ pRSMsg->ifam_type], index); + } +#endif + +mDNSlocal mDNSu32 ProcessRoutingNotification( int sd) +// Read through the messages on sd and if any indicate that any interface records should +// be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. + { + ssize_t readCount; + char buff[ 4096]; + struct ifa_msghdr *pRSMsg = (struct ifa_msghdr*) buff; + mDNSu32 result = 0; + + readCount = read( sd, buff, sizeof buff); + if ( readCount < (ssize_t) sizeof( struct ifa_msghdr)) + return mStatus_UnsupportedErr; // cannot decipher message + +#if MDNS_DEBUGMSGS + PrintRoutingSocketMsg( pRSMsg); +#endif + + // Process the message + if ( pRSMsg->ifam_type == RTM_NEWADDR || pRSMsg->ifam_type == RTM_DELADDR || + pRSMsg->ifam_type == RTM_IFINFO) + { + if ( pRSMsg->ifam_type == RTM_IFINFO) + result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index; + else + result |= 1 << pRSMsg->ifam_index; + } + + return result; + } + +#endif // USES_NETLINK + +// Called when data appears on interface change notification socket +mDNSlocal void InterfaceChangeCallback( void *context) + { + IfChangeRec *pChgRec = (IfChangeRec*) context; + fd_set readFDs; + mDNSu32 changedInterfaces = 0; + struct timeval zeroTimeout = { 0, 0 }; + + FD_ZERO( &readFDs); + FD_SET( pChgRec->NotifySD, &readFDs); + + do + { + changedInterfaces |= ProcessRoutingNotification( pChgRec->NotifySD); + } + while ( 0 < select( pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)); + + // Currently we rebuild the entire interface list whenever any interface change is + // detected. If this ever proves to be a performance issue in a multi-homed + // configuration, more care should be paid to changedInterfaces. + if ( changedInterfaces) + mDNSPlatformPosixRefreshInterfaceList( pChgRec->mDNS); + } + +// Register with either a Routing Socket or RtNetLink to listen for interface changes. +mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m) + { + mStatus err; + IfChangeRec *pChgRec; + + pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate( sizeof *pChgRec); + if ( pChgRec == NULL) + return mStatus_NoMemoryErr; + + pChgRec->mDNS = m; + err = OpenIfNotifySocket( &pChgRec->NotifySD); + if ( err == 0) + err = mDNSPosixAddFDToEventLoop( pChgRec->NotifySD, InterfaceChangeCallback, pChgRec); + + return err; + } + +// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. +// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- +// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. +mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) + { + int err; + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + struct sockaddr_in s5353; + s5353.sin_family = AF_INET; + s5353.sin_port = MulticastDNSPort.NotAnInteger; + s5353.sin_addr.s_addr = 0; + err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); + close(s); + if (err) debugf("No unicast UDP responses"); + else debugf("Unicast UDP responses okay"); + return(err == 0); + } + +// mDNS core calls this routine to initialise the platform-specific data. +mDNSexport mStatus mDNSPlatformInit(mDNS *const m) + { + int err = 0; + struct sockaddr sa; + assert(m != NULL); + + if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; + + // Tell mDNS core the names of this machine. + + // Set up the nice label + m->nicelabel.c[0] = 0; + GetUserSpecifiedFriendlyComputerName(&m->nicelabel); + if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Computer"); + + // Set up the RFC 1034-compliant label + m->hostlabel.c[0] = 0; + GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); + if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer"); + + mDNS_SetFQDN(m); + + sa.sa_family = AF_INET; + m->p->unicastSocket4 = -1; + if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4); +#if HAVE_IPV6 + sa.sa_family = AF_INET6; + m->p->unicastSocket6 = -1; + if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6); +#endif + + // Tell mDNS core about the network interfaces on this machine. + if (err == mStatus_NoError) err = SetupInterfaceList(m); + + // Tell mDNS core about DNS Servers + if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE); + + if (err == mStatus_NoError) + { + err = WatchForInterfaceChange(m); + // Failure to observe interface changes is non-fatal. + if ( err != mStatus_NoError) + { + fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", getpid(), err); + err = mStatus_NoError; + } + } + + // We don't do asynchronous initialization on the Posix platform, so by the time + // we get here the setup will already have succeeded or failed. If it succeeded, + // we should just call mDNSCoreInitComplete() immediately. + if (err == mStatus_NoError) + mDNSCoreInitComplete(m, mStatus_NoError); + + return PosixErrorToStatus(err); + } + +// mDNS core calls this routine to clean up the platform-specific data. +// In our case all we need to do is to tear down every network interface. +mDNSexport void mDNSPlatformClose(mDNS *const m) + { + assert(m != NULL); + ClearInterfaceList(m); + if (m->p->unicastSocket4 != -1) assert(close(m->p->unicastSocket4) == 0); +#if HAVE_IPV6 + if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0); +#endif + } + +mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) + { + int err; + ClearInterfaceList(m); + err = SetupInterfaceList(m); + return PosixErrorToStatus(err); + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Locking +#endif + +// On the Posix platform, locking is a no-op because we only ever enter +// mDNS core on the main thread. + +// mDNS core calls this routine when it wants to prevent +// the platform from reentering mDNS core code. +mDNSexport void mDNSPlatformLock (const mDNS *const m) + { + (void) m; // Unused + } + +// mDNS core calls this routine when it release the lock taken by +// mDNSPlatformLock and allow the platform to reenter mDNS core code. +mDNSexport void mDNSPlatformUnlock (const mDNS *const m) + { + (void) m; // Unused + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Strings +#endif + +// mDNS core calls this routine to copy C strings. +// On the Posix platform this maps directly to the ANSI C strcpy. +mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) + { + strcpy((char *)dst, (char *)src); + } + +// mDNS core calls this routine to get the length of a C string. +// On the Posix platform this maps directly to the ANSI C strlen. +mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) + { + return strlen((char*)src); + } + +// mDNS core calls this routine to copy memory. +// On the Posix platform this maps directly to the ANSI C memcpy. +mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len) + { + memcpy(dst, src, len); + } + +// mDNS core calls this routine to test whether blocks of memory are byte-for-byte +// identical. On the Posix platform this is a simple wrapper around ANSI C memcmp. +mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) + { + return memcmp(dst, src, len) == 0; + } + +// mDNS core calls this routine to clear blocks of memory. +// On the Posix platform this is a simple wrapper around ANSI C memset. +mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) + { + memset(dst, 0, len); + } + +mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); } +mDNSexport void mDNSPlatformMemFree (void *mem) { free(mem); } + +mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) + { + struct timeval tv; + gettimeofday(&tv, NULL); + return(tv.tv_usec); + } + +mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024; + +mDNSexport mStatus mDNSPlatformTimeInit(void) + { + // No special setup is required on Posix -- we just use gettimeofday(); + // This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time + // We should find a better way to do this + return(mStatus_NoError); + } + +mDNSexport mDNSs32 mDNSPlatformRawTime() + { + struct timeval tv; + gettimeofday(&tv, NULL); + // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) + // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) + // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result + // and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits. + // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) + // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). + return( (tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625) ); + } + +mDNSexport mDNSs32 mDNSPlatformUTC(void) + { + return time(NULL); + } + +mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s) + { + if (*nfds < s + 1) *nfds = s + 1; + FD_SET(s, readfds); + } + +mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout) + { + mDNSs32 ticks; + struct timeval interval; + + // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do + mDNSs32 nextevent = mDNS_Execute(m); + + // 2. Build our list of active file descriptors + PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces); + if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4); +#if HAVE_IPV6 + if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6); +#endif + while (info) + { + if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); +#if HAVE_IPV6 + if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); +#endif + info = (PosixNetworkInterface *)(info->coreIntf.next); + } + + // 3. Calculate the time remaining to the next scheduled event (in struct timeval format) + ticks = nextevent - mDNS_TimeNow(m); + if (ticks < 1) ticks = 1; + interval.tv_sec = ticks >> 10; // The high 22 bits are seconds + interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16; // The low 10 bits are 1024ths + + // 4. If client's proposed timeout is more than what we want, then reduce it + if (timeout->tv_sec > interval.tv_sec || + (timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec)) + *timeout = interval; + } + +mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds) + { + PosixNetworkInterface *info; + assert(m != NULL); + assert(readfds != NULL); + info = (PosixNetworkInterface *)(m->HostInterfaces); + + if (m->p->unicastSocket4 != -1 && FD_ISSET(m->p->unicastSocket4, readfds)) + { + FD_CLR(m->p->unicastSocket4, readfds); + SocketDataReady(m, NULL, m->p->unicastSocket4); + } +#if HAVE_IPV6 + if (m->p->unicastSocket6 != -1 && FD_ISSET(m->p->unicastSocket6, readfds)) + { + FD_CLR(m->p->unicastSocket6, readfds); + SocketDataReady(m, NULL, m->p->unicastSocket6); + } +#endif + + while (info) + { + if (info->multicastSocket4 != -1 && FD_ISSET(info->multicastSocket4, readfds)) + { + FD_CLR(info->multicastSocket4, readfds); + SocketDataReady(m, info, info->multicastSocket4); + } +#if HAVE_IPV6 + if (info->multicastSocket6 != -1 && FD_ISSET(info->multicastSocket6, readfds)) + { + FD_CLR(info->multicastSocket6, readfds); + SocketDataReady(m, info, info->multicastSocket6); + } +#endif + info = (PosixNetworkInterface *)(info->coreIntf.next); + } + } + +// update gMaxFD +mDNSlocal void DetermineMaxEventFD( void ) + { + PosixEventSource *iSource; + + gMaxFD = 0; + for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + if ( gMaxFD < iSource->fd) + gMaxFD = iSource->fd; + } + +// Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to. +mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context) + { + PosixEventSource *newSource; + + if ( gEventSources.LinkOffset == 0) + InitLinkedList( &gEventSources, offsetof( PosixEventSource, Next)); + + if ( fd >= (int) FD_SETSIZE || fd < 0) + return mStatus_UnsupportedErr; + if ( callback == NULL) + return mStatus_BadParamErr; + + newSource = (PosixEventSource*) malloc( sizeof *newSource); + if ( NULL == newSource) + return mStatus_NoMemoryErr; + + newSource->Callback = callback; + newSource->Context = context; + newSource->fd = fd; + + AddToTail( &gEventSources, newSource); + FD_SET( fd, &gEventFDs); + + DetermineMaxEventFD(); + + return mStatus_NoError; + } + +// Remove a file descriptor from the set that mDNSPosixRunEventLoopOnce() listens to. +mStatus mDNSPosixRemoveFDFromEventLoop( int fd) + { + PosixEventSource *iSource; + + for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + { + if ( fd == iSource->fd) + { + FD_CLR( fd, &gEventFDs); + RemoveFromList( &gEventSources, iSource); + free( iSource); + DetermineMaxEventFD(); + return mStatus_NoError; + } + } + return mStatus_NoSuchNameErr; + } + +// Simply note the received signal in gEventSignals. +mDNSlocal void NoteSignal( int signum) + { + sigaddset( &gEventSignals, signum); + } + +// Tell the event package to listen for signal and report it in mDNSPosixRunEventLoopOnce(). +mStatus mDNSPosixListenForSignalInEventLoop( int signum) + { + struct sigaction action; + mStatus err; + + bzero( &action, sizeof action); // more portable than member-wise assignment + action.sa_handler = NoteSignal; + err = sigaction( signum, &action, (struct sigaction*) NULL); + + sigaddset( &gEventSignalSet, signum); + + return err; + } + +// Tell the event package to stop listening for signal in mDNSPosixRunEventLoopOnce(). +mStatus mDNSPosixIgnoreSignalInEventLoop( int signum) + { + struct sigaction action; + mStatus err; + + bzero( &action, sizeof action); // more portable than member-wise assignment + action.sa_handler = SIG_DFL; + err = sigaction( signum, &action, (struct sigaction*) NULL); + + sigdelset( &gEventSignalSet, signum); + + return err; + } + +// Do a single pass through the attendent event sources and dispatch any found to their callbacks. +// Return as soon as internal timeout expires, or a signal we're listening for is received. +mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, + sigset_t *pSignalsReceived, mDNSBool *pDataDispatched) + { + fd_set listenFDs = gEventFDs; + int fdMax = 0, numReady; + struct timeval timeout = *pTimeout; + + // Include the sockets that are listening to the wire in our select() set + mDNSPosixGetFDSet( m, &fdMax, &listenFDs, &timeout); // timeout may get modified + if ( fdMax < gMaxFD) + fdMax = gMaxFD; + + numReady = select( fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout); + + // If any data appeared, invoke its callback + if ( numReady > 0) + { + PosixEventSource *iSource; + + (void) mDNSPosixProcessFDSet( m, &listenFDs); // call this first to process wire data for clients + + for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + { + if ( FD_ISSET( iSource->fd, &listenFDs)) + { + iSource->Callback( iSource->Context); + break; // in case callback removed elements from gEventSources + } + } + *pDataDispatched = mDNStrue; + } + else + *pDataDispatched = mDNSfalse; + + (void) sigprocmask( SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL); + *pSignalsReceived = gEventSignals; + sigemptyset( &gEventSignals); + (void) sigprocmask( SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL); + + return mStatus_NoError; + } diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h new file mode 100644 index 0000000000..bd5286c899 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h @@ -0,0 +1,147 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: mDNSPosix.h,v $ +Revision 1.18 2006/08/14 23:24:47 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.17 2005/02/04 00:39:59 cheshire +Move ParseDNSServers() from PosixDaemon.c to mDNSPosix.c so all Posix client layers can use it + +Revision 1.16 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.15 2004/02/06 01:19:51 cheshire +Conditionally exclude IPv6 code unless HAVE_IPV6 is set + +Revision 1.14 2004/01/28 21:12:15 cheshire +Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6) + +Revision 1.13 2004/01/24 05:12:03 cheshire +<rdar://problem/3534352>: Need separate socket for issuing unicast queries + +Revision 1.12 2004/01/23 21:37:08 cheshire +For consistency, rename multicastSocket to multicastSocket4, and multicastSocketv6 to multicastSocket6 + +Revision 1.11 2003/12/11 03:03:51 rpantos +Clean up mDNSPosix so that it builds on OS X again. + +Revision 1.10 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + +Revision 1.9 2003/10/30 19:25:19 cheshire +Fix warning on certain compilers + +Revision 1.8 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.7 2003/07/02 21:19:59 cheshire +<rdar://problem/3313413> Update copyright notices, etc., in source code comments + +Revision 1.6 2003/03/13 03:46:21 cheshire +Fixes to make the code build on Linux + +Revision 1.5 2003/03/08 00:35:56 cheshire +Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.4 2002/12/23 22:13:31 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 06:24:34 cheshire +First checkin + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __mDNSPlatformPosix_h +#define __mDNSPlatformPosix_h + +#include <signal.h> +#include <sys/time.h> + +#ifdef __cplusplus + extern "C" { +#endif + +// PosixNetworkInterface is a record extension of the core NetworkInterfaceInfo +// type that supports extra fields needed by the Posix platform. +// +// IMPORTANT: coreIntf must be the first field in the structure because +// we cast between pointers to the two different types regularly. + +typedef struct PosixNetworkInterface PosixNetworkInterface; + +struct PosixNetworkInterface + { + NetworkInterfaceInfo coreIntf; + const char * intfName; + PosixNetworkInterface * aliasIntf; + int index; + int multicastSocket4; +#if HAVE_IPV6 + int multicastSocket6; +#endif + }; + +// This is a global because debugf_() needs to be able to check its value +extern int gMDNSPlatformPosixVerboseLevel; + +struct mDNS_PlatformSupport_struct + { + int unicastSocket4; +#if HAVE_IPV6 + int unicastSocket6; +#endif + }; + +#define uDNS_SERVERS_FILE "/etc/resolv.conf" +extern int ParseDNSServers(mDNS *m, const char *filePath); +extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m); + // See comment in implementation. + +// Call mDNSPosixGetFDSet before calling select(), to update the parameters +// as may be necessary to meet the needs of the mDNSCore code. +// The timeout pointer MUST NOT be NULL. +// Set timeout->tv_sec to 0x3FFFFFFF if you want to have effectively no timeout +// After calling mDNSPosixGetFDSet(), call select(nfds, &readfds, NULL, NULL, &timeout); as usual +// After select() returns, call mDNSPosixProcessFDSet() to let mDNSCore do its work +extern void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout); +extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds); + +typedef void (*mDNSPosixEventCallback)( void *context); + +extern mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context); +extern mStatus mDNSPosixRemoveFDFromEventLoop( int fd); +extern mStatus mDNSPosixListenForSignalInEventLoop( int signum); +extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum); +extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c new file mode 100644 index 0000000000..15db1b7539 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c @@ -0,0 +1,1102 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: mDNSUNP.c,v $ +Revision 1.34 2006/08/14 23:24:47 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.33 2006/03/13 23:14:21 cheshire +<rdar://problem/4427969> Compile problems on FreeBSD +Use <netinet/in_var.h> instead of <netinet6/in6_var.h> + +Revision 1.32 2005/12/21 02:56:43 cheshire +<rdar://problem/4243433> get_ifi_info() should fake ifi_index when SIOCGIFINDEX undefined + +Revision 1.31 2005/12/21 02:46:05 cheshire +<rdar://problem/4243514> mDNSUNP.c needs to include <sys/param.h> on 4.4BSD Lite + +Revision 1.30 2005/11/29 20:03:02 mkrochma +Wrapped sin_len with #ifndef NOT_HAVE_SA_LEN + +Revision 1.29 2005/11/12 02:23:10 cheshire +<rdar://problem/4317680> mDNSUNP.c needs to deal with lame results from SIOCGIFNETMASK, SIOCGIFBRDADDR and SIOCGIFDSTADDR + +Revision 1.28 2005/10/31 22:09:45 cheshire +Buffer "char addr6[33]" was seven bytes too small + +Revision 1.27 2005/06/29 15:54:21 cheshire +<rdar://problem/4113742> mDNSResponder-107.1 does not work on FreeBSD +Refine last checkin so that it (hopefully) doesn't break get_ifi_info() for every other OS + +Revision 1.26 2005/04/08 21:43:59 ksekar +<rdar://problem/4083426> mDNSPosix (v98) retrieve interface list bug on AMD64 architecture +Submitted by Andrew de Quincey + +Revision 1.25 2005/04/08 21:37:57 ksekar +<rdar://problem/3792767> get_ifi_info doesn't return IPv6 interfaces on Linux + +Revision 1.24 2005/04/08 21:30:16 ksekar +<rdar://problem/4007457> Compiling problems with mDNSResponder-98 on Solaris/Sparc v9 +Patch submitted by Bernd Kuhls + +Revision 1.23 2004/12/01 04:25:05 cheshire +<rdar://problem/3872803> Darwin patches for Solaris and Suse +Provide daemon() for platforms that don't have it + +Revision 1.22 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.21 2004/11/08 22:13:59 rpantos +Create sockf6 lazily when v6 interface found. + +Revision 1.20 2004/10/16 00:17:01 cheshire +<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check + +Revision 1.19 2004/07/20 01:47:36 rpantos +NOT_HAVE_SA_LEN applies to v6, too. And use more-portable s6_addr. + +Revision 1.18 2004/07/08 21:30:21 rpantos + +Revision 1.17 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.16 2004/03/20 05:37:09 cheshire +Fix contributed by Terry Lambert & Alfred Perlstein: +Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x + +Revision 1.15 2004/02/14 01:09:45 rpantos +Just use HAVE_IPV6 rather than defined(HAVE_IPV6). + +Revision 1.14 2003/12/11 18:53:40 cheshire +Fix compiler warning reported by Paul Guyot + +Revision 1.13 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + +Revision 1.12 2003/09/02 20:47:13 cheshire +Fix signed/unsigned warning + +Revision 1.11 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.10 2003/08/06 18:20:51 cheshire +Makefile cleanup + +Revision 1.9 2003/07/14 18:11:54 cheshire +Fix stricter compiler warnings + +Revision 1.8 2003/07/02 21:19:59 cheshire +<rdar://problem/3313413> Update copyright notices, etc., in source code comments + +Revision 1.7 2003/03/20 21:10:31 cheshire +Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris + +Revision 1.6 2003/03/13 03:46:21 cheshire +Fixes to make the code build on Linux + +Revision 1.5 2003/02/07 03:02:02 cheshire +Submitted by: Mitsutaka Watanabe +The code saying "index += 1;" was effectively making up random interface index values. +The right way to find the correct interface index is if_nametoindex(); + +Revision 1.4 2002/12/23 22:13:31 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 06:24:34 cheshire +First checkin + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mDNSUNP.h" + +#include "mDNSDebug.h" + +#include <errno.h> +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <sys/uio.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdio.h> + +/* Some weird platforms derived from 4.4BSD Lite (e.g. EFI) need the ALIGN(P) + macro, usually defined in <sys/param.h> or someplace like that, to make sure the + CMSG_NXTHDR macro is well-formed. On such platforms, the symbol NEED_ALIGN_MACRO + should be set to the name of the header to include to get the ALIGN(P) macro. +*/ +#ifdef NEED_ALIGN_MACRO +#include NEED_ALIGN_MACRO +#endif + +/* Solaris defined SIOCGIFCONF etc in <sys/sockio.h> but + other platforms don't even have that include file. So, + if we haven't yet got a definition, let's try to find + <sys/sockio.h>. +*/ + +#ifndef SIOCGIFCONF + #include <sys/sockio.h> +#endif + +/* sockaddr_dl is only referenced if we're using IP_RECVIF, + so only include the header in that case. +*/ + +#ifdef IP_RECVIF + #include <net/if_dl.h> +#endif + +#if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX +#if !HAVE_SOLARIS +#include <net/if_var.h> +#else +#include <alloca.h> +#ifdef HAVE_SOLARIS_ZONES +#include <zone.h> +#endif /* HAVE_SOLARIS_ZONES */ +#endif /* !HAVE_SOLARIS */ +#include <netinet/in_var.h> +// NOTE: netinet/in_var.h implicitly includes netinet6/in6_var.h for us +#endif + +#if defined(AF_INET6) && HAVE_IPV6 + +#if HAVE_LINUX +#include <netdb.h> +#include <arpa/inet.h> + +/* Converts a prefix length to IPv6 network mask */ +void plen_to_mask(int plen, char *addr) { + int i; + int colons=7; /* Number of colons in IPv6 address */ + int bits_in_block=16; /* Bits per IPv6 block */ + for(i=0;i<=colons;i++) { + int block, ones=0xffff, ones_in_block; + if(plen>bits_in_block) ones_in_block=bits_in_block; + else ones_in_block=plen; + block = ones & (ones << (bits_in_block-ones_in_block)); + i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block); + plen -= ones_in_block; + } + } + +/* Gets IPv6 interface information from the /proc filesystem in linux*/ +struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) + { + struct ifi_info *ifi, *ifihead, **ifipnext; + FILE *fp; + char addr[8][5]; + int flags, myflags, index, plen, scope; + char ifname[8], lastname[IFNAMSIZ]; + char addr6[32+7+1]; /* don't forget the seven ':' */ + struct addrinfo hints, *res0; + struct sockaddr_in6 *sin6; + struct in6_addr *addrptr; + int err; + + res0=NULL; + ifihead = NULL; + ifipnext = &ifihead; + lastname[0] = 0; + + if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) { + while (fscanf(fp, + "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %8s\n", + addr[0],addr[1],addr[2],addr[3], + addr[4],addr[5],addr[6],addr[7], + &index, &plen, &scope, &flags, ifname) != EOF) { + + myflags = 0; + if (strncmp(lastname, ifname, IFNAMSIZ) == 0) { + if (doaliases == 0) + continue; /* already processed this interface */ + myflags = IFI_ALIAS; + } + memcpy(lastname, ifname, IFNAMSIZ); + ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); + if (ifi == NULL) { + goto gotError; + } + + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ + + sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", + addr[0],addr[1],addr[2],addr[3], + addr[4],addr[5],addr[6],addr[7]); + + /* Add address of the interface */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + err = getaddrinfo(addr6, NULL, &hints, &res0); + if (err) { + goto gotError; + } + ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6)); + + /* Add netmask of the interface */ + char ipv6addr[INET6_ADDRSTRLEN]; + plen_to_mask(plen, ipv6addr); + ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + sin6=calloc(1, sizeof(struct sockaddr_in6)); + addrptr=calloc(1, sizeof(struct in6_addr)); + inet_pton(family, ipv6addr, addrptr); + sin6->sin6_family=family; + sin6->sin6_addr=*addrptr; + sin6->sin6_scope_id=scope; + memcpy(ifi->ifi_netmask, sin6, sizeof(struct sockaddr_in6)); + free(sin6); + + + /* Add interface name */ + memcpy(ifi->ifi_name, ifname, IFI_NAME); + + /* Add interface index */ + ifi->ifi_index = index; + + /* If interface is in /proc then it is up*/ + ifi->ifi_flags = IFF_UP; + + freeaddrinfo(res0); + res0=NULL; + } + } + goto done; + + gotError: + if (ifihead != NULL) { + free_ifi_info(ifihead); + ifihead = NULL; + } + if (res0 != NULL) { + freeaddrinfo(res0); + res0=NULL; + } + done: + return(ifihead); /* pointer to first structure in linked list */ + } + +#endif /* LINUX */ +#endif /* defined(AF_INET6) && HAVE_IPV6 */ + +#if HAVE_SOLARIS + +/* + * Converts prefix length to network mask. Assumes + * addr points to a zeroed out buffer and prefix <= sizeof(addr) + * Unlike plen_to_mask returns netmask in binary form and not + * in text form. + */ +static void plen_to_netmask(int prefix, unsigned char *addr) { + for (; prefix > 8; prefix -= 8) + *addr++ = 0xff; + for (; prefix > 0; prefix--) + *addr = (*addr >> 1) | 0x80; +} + +/* + * This function goes through all the IP interfaces associated with a + * physical interface and finds the best matched one for use by mDNS. + * Returns NULL when none of the IP interfaces associated with a physical + * interface are usable. Otherwise returns the best matched interface + * information and a pointer to the best matched lifreq. + */ +struct ifi_info * +select_src_ifi_info_solaris(int sockfd, int numifs, + struct lifreq *lifrlist, const char *curifname, + struct lifreq **best_lifr) +{ + struct lifreq *lifr; + struct lifreq lifrcopy; + struct ifi_info *ifi; + char *chptr; + char cmpifname[LIFNAMSIZ]; + int i; + uint64_t best_lifrflags; + uint64_t ifflags; + + *best_lifr = NULL; + + /* + * Check all logical interfaces associated with the physical + * interface and figure out which one works best for us. + */ + for (i = numifs, lifr = lifrlist; i > 0; --i, ++lifr) { + + if (strlcpy(cmpifname, lifr->lifr_name, sizeof(cmpifname)) >= sizeof(cmpifname)) + continue; /* skip interface */ + + /* Strip logical interface number before checking ifname */ + if ((chptr = strchr(cmpifname, ':')) != NULL) + *chptr = '\0'; + + /* + * Check ifname to see if the logical interface is associated + * with the physical interface we are interested in. + */ + if (strcmp(cmpifname, curifname) != 0) + continue; + +#ifdef HAVE_SOLARIS_ZONES + /* Check the zone associated with the address */ + lifrcopy = *lifr; + if (ioctl(sockfd, SIOCGLIFZONE, &lifrcopy) < 0) { + /* interface removed */ + if (errno == ENXIO) + continue; + return(NULL); + } + if (lifrcopy.lifr_zoneid != getzoneid()) + continue; +#endif + + lifrcopy = *lifr; + if (ioctl(sockfd, SIOCGLIFFLAGS, &lifrcopy) < 0) { + /* interface removed */ + if (errno == ENXIO) + continue; + return(NULL); + } + ifflags = lifrcopy.lifr_flags; + + /* ignore address if not up */ + if ((ifflags & IFF_UP) == 0) + continue; + /* + * Avoid address if any of the following flags are set: + * IFF_NOFAILOVER: IPMP test address for use only by in.mpathd + * IFF_NOXMIT: no packets transmitted over interface + * IFF_NOLOCAL: no address + * IFF_PRIVATE: is not advertised + */ + if (ifflags & (IFF_NOFAILOVER | IFF_NOXMIT + | IFF_NOLOCAL | IFF_PRIVATE)) + continue; + + if (*best_lifr != NULL) { + /* + * Check if we found a better interface by checking + * the flags. If flags are identical we prefer + * the new found interface. + */ + uint64_t diff_flags = best_lifrflags ^ ifflags; + + /* If interface has a different set of flags */ + if (diff_flags != 0) { + /* Check flags in increasing order of ones we prefer */ + + /* Address temporary? */ + if ((diff_flags & IFF_TEMPORARY) && + (ifflags & IFF_TEMPORARY)) + continue; + /* Deprecated address? */ + if ((diff_flags & IFF_DEPRECATED) && + (ifflags & IFF_DEPRECATED)) + continue; + /* Last best-matched interface address has preferred? */ + if ((diff_flags & IFF_PREFERRED) && + ((ifflags & IFF_PREFERRED) == 0)) + continue; + } + } + + /* Set best match interface & flags */ + *best_lifr = lifr; + best_lifrflags = ifflags; + } + + if (*best_lifr == NULL) + return(NULL); + + /* Found a match: return the interface information */ + ifi = calloc(1, sizeof(struct ifi_info)); + if (ifi == NULL) + return(NULL); + + ifi->ifi_flags = best_lifrflags; + ifi->ifi_index = if_nametoindex((*best_lifr)->lifr_name); + if (strlcpy(ifi->ifi_name, (*best_lifr)->lifr_name, sizeof(ifi->ifi_name)) >= sizeof(ifi->ifi_name)) { + free(ifi); + return(NULL); + } + return(ifi); +} + +/* + * Returns a list of IP interface information on Solaris. The function + * returns all IP interfaces on the system with IPv4 address assigned + * when passed AF_INET and returns IP interfaces with IPv6 address assigned + * when AF_INET6 is passed. + */ +struct ifi_info *get_ifi_info_solaris(int family) +{ + struct ifi_info *ifi, *ifihead, **ifipnext; + int sockfd; + int len; + char *buf; + char *cptr; + char ifname[LIFNAMSIZ], cmpifname[LIFNAMSIZ]; + struct sockaddr_in *sinptr; + struct lifnum lifn; + struct lifconf lifc; + struct lifreq *lifrp, *best_lifr; + struct lifreq lifrcopy; + int numifs, nlifr, n; +#if defined(AF_INET6) && HAVE_IPV6 + struct sockaddr_in6 *sinptr6; +#endif + + ifihead = NULL; + + sockfd = socket(family, SOCK_DGRAM, 0); + if (sockfd < 0) + goto gotError; + +again: + lifn.lifn_family = family; + lifn.lifn_flags = 0; + if (ioctl(sockfd, SIOCGLIFNUM, &lifn) < 0) + goto gotError; + /* + * Pad interface count to detect & retrieve any + * additional interfaces between IFNUM & IFCONF calls. + */ + lifn.lifn_count += 4; + numifs = lifn.lifn_count; + len = numifs * sizeof (struct lifreq); + buf = alloca(len); + + lifc.lifc_family = family; + lifc.lifc_len = len; + lifc.lifc_buf = buf; + lifc.lifc_flags = 0; + + if (ioctl(sockfd, SIOCGLIFCONF, &lifc) < 0) + goto gotError; + + nlifr = lifc.lifc_len / sizeof(struct lifreq); + if (nlifr >= numifs) + goto again; + + lifrp = lifc.lifc_req; + ifipnext = &ifihead; + + for (n = nlifr; n > 0; n--, lifrp++) { + + if (lifrp->lifr_addr.ss_family != family) + continue; + + /* + * See if we have already processed the interface + * by checking the interface names. + */ + if (strlcpy(ifname, lifrp->lifr_name, sizeof(ifname)) >= sizeof(ifname)) + goto gotError; + if ((cptr = strchr(ifname, ':')) != NULL) + *cptr = '\0'; + + /* + * If any of the interfaces found so far share the physical + * interface name then we have already processed the interface. + */ + for (ifi = ifihead; ifi != NULL; ifi = ifi->ifi_next) { + + /* Retrieve physical interface name */ + (void) strlcpy(cmpifname, ifi->ifi_name, sizeof(cmpifname)); + + /* Strip logical interface number before checking ifname */ + if ((cptr = strchr(cmpifname, ':')) != NULL) + *cptr = '\0'; + + if (strcmp(cmpifname, ifname) == 0) + break; + } + if (ifi != NULL) + continue; /* already processed */ + + /* + * New interface, find the one with the preferred source + * address for our use in Multicast DNS. + */ + if ((ifi = select_src_ifi_info_solaris(sockfd, nlifr, + lifc.lifc_req, ifname, &best_lifr)) == NULL) + continue; + + assert(best_lifr != NULL); + assert((best_lifr->lifr_addr.ss_family == AF_INET6) || + (best_lifr->lifr_addr.ss_family == AF_INET)); + + switch (best_lifr->lifr_addr.ss_family) { + +#if defined(AF_INET6) && HAVE_IPV6 + case AF_INET6: + sinptr6 = (struct sockaddr_in6 *) &best_lifr->lifr_addr; + ifi->ifi_addr = malloc(sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) + goto gotError; + memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6)); + + ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_netmask == NULL) + goto gotError; + sinptr6 = (struct sockaddr_in6 *)(ifi->ifi_netmask); + sinptr6->sin6_family = AF_INET6; + plen_to_netmask(best_lifr->lifr_addrlen, + (unsigned char *) &(sinptr6->sin6_addr)); + break; +#endif + + case AF_INET: + sinptr = (struct sockaddr_in *) &best_lifr->lifr_addr; + ifi->ifi_addr = malloc(sizeof(struct sockaddr_in)); + if (ifi->ifi_addr == NULL) + goto gotError; + + memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); + + lifrcopy = *best_lifr; + if (ioctl(sockfd, SIOCGLIFNETMASK, &lifrcopy) < 0) { + /* interface removed */ + if (errno == ENXIO) { + free(ifi->ifi_addr); + free(ifi); + continue; + } + goto gotError; + } + + ifi->ifi_netmask = malloc(sizeof(struct sockaddr_in)); + if (ifi->ifi_netmask == NULL) + goto gotError; + sinptr = (struct sockaddr_in *) &lifrcopy.lifr_addr; + sinptr->sin_family = AF_INET; + memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); + break; + + default: + /* never reached */ + break; + } + + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ + } + + (void) close(sockfd); + return(ifihead); /* pointer to first structure in linked list */ + +gotError: + if (sockfd != -1) + (void) close(sockfd); + if (ifihead != NULL) + free_ifi_info(ifihead); + return(NULL); +} + +#endif /* HAVE_SOLARIS */ + +struct ifi_info *get_ifi_info(int family, int doaliases) +{ + int junk; + struct ifi_info *ifi, *ifihead, **ifipnext; + int sockfd, sockf6, len, lastlen, flags, myflags; +#ifdef NOT_HAVE_IF_NAMETOINDEX + int index = 200; +#endif + char *ptr, *buf, lastname[IFNAMSIZ], *cptr; + struct ifconf ifc; + struct ifreq *ifr, ifrcopy; + struct sockaddr_in *sinptr; + +#if defined(AF_INET6) && HAVE_IPV6 + struct sockaddr_in6 *sinptr6; +#endif + +#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX + if(family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); +#elif HAVE_SOLARIS + return get_ifi_info_solaris(family); +#endif + + sockfd = -1; + sockf6 = -1; + buf = NULL; + ifihead = NULL; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + goto gotError; + } + + lastlen = 0; + len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ + for ( ; ; ) { + buf = (char*)malloc(len); + if (buf == NULL) { + goto gotError; + } + ifc.ifc_len = len; + ifc.ifc_buf = buf; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { + if (errno != EINVAL || lastlen != 0) { + goto gotError; + } + } else { + if (ifc.ifc_len == lastlen) + break; /* success, len has not changed */ + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + free(buf); + } + ifihead = NULL; + ifipnext = &ifihead; + lastname[0] = 0; +/* end get_ifi_info1 */ + +/* include get_ifi_info2 */ + for (ptr = buf; ptr < buf + ifc.ifc_len; ) { + ifr = (struct ifreq *) ptr; + + /* Advance to next one in buffer */ + if (sizeof(struct ifreq) > sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr)) + ptr += sizeof(struct ifreq); + else + ptr += sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr); + +// fprintf(stderr, "intf %p name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family); + + if (ifr->ifr_addr.sa_family != family) + continue; /* ignore if not desired address family */ + + myflags = 0; + if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) + *cptr = 0; /* replace colon will null */ + if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { + if (doaliases == 0) + continue; /* already processed this interface */ + myflags = IFI_ALIAS; + } + memcpy(lastname, ifr->ifr_name, IFNAMSIZ); + + ifrcopy = *ifr; + if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) { + goto gotError; + } + + flags = ifrcopy.ifr_flags; + if ((flags & IFF_UP) == 0) + continue; /* ignore if interface not up */ + + ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); + if (ifi == NULL) { + goto gotError; + } + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ + + ifi->ifi_flags = flags; /* IFF_xxx values */ + ifi->ifi_myflags = myflags; /* IFI_xxx values */ +#ifndef NOT_HAVE_IF_NAMETOINDEX + ifi->ifi_index = if_nametoindex(ifr->ifr_name); +#else + ifrcopy = *ifr; +#ifdef SIOCGIFINDEX + if ( 0 >= ioctl(sockfd, SIOCGIFINDEX, &ifrcopy)) + ifi->ifi_index = ifrcopy.ifr_index; + else +#endif + ifi->ifi_index = index++; /* SIOCGIFINDEX is broken on Solaris 2.5ish, so fake it */ +#endif + memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME); + ifi->ifi_name[IFI_NAME-1] = '\0'; +/* end get_ifi_info2 */ +/* include get_ifi_info3 */ + switch (ifr->ifr_addr.sa_family) { + case AF_INET: + sinptr = (struct sockaddr_in *) &ifr->ifr_addr; + if (ifi->ifi_addr == NULL) { + ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); + +#ifdef SIOCGIFNETMASK + if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) goto gotError; + ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_netmask == NULL) goto gotError; + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_addr; + /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ +#ifndef NOT_HAVE_SA_LEN + sinptr->sin_len = sizeof(struct sockaddr_in); +#endif + sinptr->sin_family = AF_INET; + memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in)); +#endif + +#ifdef SIOCGIFBRDADDR + if (flags & IFF_BROADCAST) { + if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) { + goto gotError; + } + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; + /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ +#ifndef NOT_HAVE_SA_LEN + sinptr->sin_len = sizeof( struct sockaddr_in ); +#endif + sinptr->sin_family = AF_INET; + ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_brdaddr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + +#ifdef SIOCGIFDSTADDR + if (flags & IFF_POINTOPOINT) { + if (ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy) < 0) { + goto gotError; + } + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; + /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */ +#ifndef NOT_HAVE_SA_LEN + sinptr->sin_len = sizeof( struct sockaddr_in ); +#endif + sinptr->sin_family = AF_INET; + ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_dstaddr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + } + break; + +#if defined(AF_INET6) && HAVE_IPV6 + case AF_INET6: + sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr; + if (ifi->ifi_addr == NULL) { + ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + + /* Some platforms (*BSD) inject the prefix in IPv6LL addresses */ + /* We need to strip that out */ + if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr)) + sinptr6->sin6_addr.s6_addr[2] = sinptr6->sin6_addr.s6_addr[3] = 0; + memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6)); + +#ifdef SIOCGIFNETMASK_IN6 + { + struct in6_ifreq ifr6; + if (sockf6 == -1) + sockf6 = socket(AF_INET6, SOCK_DGRAM, 0); + bzero(&ifr6, sizeof(ifr6)); + memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name )); + memcpy(&ifr6.ifr_ifru.ifru_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_ifru.ifru_addr)); + if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) goto gotError; + ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_netmask == NULL) goto gotError; + sinptr6 = (struct sockaddr_in6 *) &ifr6.ifr_ifru.ifru_addr; + memcpy(ifi->ifi_netmask, sinptr6, sizeof(struct sockaddr_in6)); + } +#endif + } + break; +#endif + + default: + break; + } + } + goto done; + +gotError: + if (ifihead != NULL) { + free_ifi_info(ifihead); + ifihead = NULL; + } + +done: + if (buf != NULL) { + free(buf); + } + if (sockfd != -1) { + junk = close(sockfd); + assert(junk == 0); + } + if (sockf6 != -1) { + junk = close(sockf6); + assert(junk == 0); + } + return(ifihead); /* pointer to first structure in linked list */ +} +/* end get_ifi_info3 */ + +/* include free_ifi_info */ +void +free_ifi_info(struct ifi_info *ifihead) +{ + struct ifi_info *ifi, *ifinext; + + for (ifi = ifihead; ifi != NULL; ifi = ifinext) { + if (ifi->ifi_addr != NULL) + free(ifi->ifi_addr); + if (ifi->ifi_brdaddr != NULL) + free(ifi->ifi_brdaddr); + if (ifi->ifi_dstaddr != NULL) + free(ifi->ifi_dstaddr); + if (ifi->ifi_netmask != NULL) + free(ifi->ifi_netmask); + ifinext = ifi->ifi_next; /* can't fetch ifi_next after free() */ + free(ifi); /* the ifi_info{} itself */ + } +} +/* end free_ifi_info */ + +ssize_t +recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, + struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t n; + +#ifdef CMSG_FIRSTHDR + struct cmsghdr *cmptr; + union { + struct cmsghdr cm; + char control[1024]; + pad64_t align8; /* ensure structure is 8-byte aligned on sparc */ + } control_un; + + *ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be + + msg.msg_control = (void *) control_un.control; + msg.msg_controllen = sizeof(control_un.control); + msg.msg_flags = 0; +#else + memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */ +#endif /* CMSG_FIRSTHDR */ + + msg.msg_name = (char *) sa; + msg.msg_namelen = *salenptr; + iov[0].iov_base = (char *)ptr; + iov[0].iov_len = nbytes; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if ( (n = recvmsg(fd, &msg, *flagsp)) < 0) + return(n); + + *salenptr = msg.msg_namelen; /* pass back results */ + if (pktp) { + /* 0.0.0.0, i/f = -1 */ + /* We set the interface to -1 so that the caller can + tell whether we returned a meaningful value or + just some default. Previously this code just + set the value to 0, but I'm concerned that 0 + might be a valid interface value. + */ + memset(pktp, 0, sizeof(struct my_in_pktinfo)); + pktp->ipi_ifindex = -1; + } +/* end recvfrom_flags1 */ + +/* include recvfrom_flags2 */ +#ifndef CMSG_FIRSTHDR + #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc. + *flagsp = 0; /* pass back results */ + return(n); +#else + + *flagsp = msg.msg_flags; /* pass back results */ + if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) || + (msg.msg_flags & MSG_CTRUNC) || pktp == NULL) + return(n); + + for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; + cmptr = CMSG_NXTHDR(&msg, cmptr)) { + +#ifdef IP_PKTINFO +#if in_pktinfo_definition_is_missing +struct in_pktinfo +{ + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *tmp; + struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr; + + tmp = (struct in_pktinfo *) CMSG_DATA(cmptr); + sin->sin_family = AF_INET; + sin->sin_addr = tmp->ipi_addr; + sin->sin_port = 0; + pktp->ipi_ifindex = tmp->ipi_ifindex; + continue; + } +#endif + +#ifdef IP_RECVDSTADDR + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVDSTADDR) { + struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr; + + sin->sin_family = AF_INET; + sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr); + sin->sin_port = 0; + continue; + } +#endif + +#ifdef IP_RECVIF + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVIF) { + struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr); +#ifndef HAVE_BROKEN_RECVIF_NAME + int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1); + strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen); +#endif + (void) memcpy(&pktp->ipi_ifindex, CMSG_DATA(cmptr), sizeof(uint_t)); + assert(pktp->ipi_ifname[IFI_NAME - 1] == 0); + // null terminated because of memset above + continue; + } +#endif + +#ifdef IP_RECVTTL + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVTTL) { + *ttl = *(u_char*)CMSG_DATA(cmptr); + continue; + } + else if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_TTL) { // some implementations seem to send IP_TTL instead of IP_RECVTTL + *ttl = *(int*)CMSG_DATA(cmptr); + continue; + } +#endif + +#if defined(IPV6_PKTINFO) && HAVE_IPV6 + if (cmptr->cmsg_level == IPPROTO_IPV6 && + cmptr->cmsg_type == IPV6_PKTINFO) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr; + struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr); + + sin6->sin6_family = AF_INET6; +#ifndef NOT_HAVE_SA_LEN + sin6->sin6_len = sizeof(*sin6); +#endif + sin6->sin6_addr = ip6_info->ipi6_addr; + sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + sin6->sin6_port = 0; + pktp->ipi_ifindex = ip6_info->ipi6_ifindex; + continue; + } +#endif + +#if defined(IPV6_HOPLIMIT) && HAVE_IPV6 + if (cmptr->cmsg_level == IPPROTO_IPV6 && + cmptr->cmsg_type == IPV6_HOPLIMIT) { + *ttl = *(int*)CMSG_DATA(cmptr); + continue; + } +#endif + assert(0); // unknown ancillary data + } + return(n); +#endif /* CMSG_FIRSTHDR */ +} + +// ********************************************************************************************** + +// daemonize the process. Adapted from "Unix Network Programming" vol 1 by Stevens, section 12.4. +// Returns 0 on success, -1 on failure. + +#ifdef NOT_HAVE_DAEMON +#include <fcntl.h> +#include <sys/stat.h> +#include <signal.h> + +int daemon(int nochdir, int noclose) + { + switch (fork()) + { + case -1: return (-1); // Fork failed + case 0: break; // Child -- continue + default: _exit(0); // Parent -- exit + } + + if (setsid() == -1) return(-1); + + signal(SIGHUP, SIG_IGN); + + switch (fork()) // Fork again, primarily for reasons of Unix trivia + { + case -1: return (-1); // Fork failed + case 0: break; // Child -- continue + default: _exit(0); // Parent -- exit + } + + if (!nochdir) (void)chdir("/"); + umask(0); + + if (!noclose) + { + int fd = open("/dev/null", O_RDWR, 0); + if (fd != -1) + { + // Avoid unnecessarily duplicating a file descriptor to itself + if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO); + if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO); + if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO); + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + (void)close (fd); + } + } + return (0); + } +#endif /* NOT_HAVE_DAEMON */ diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h new file mode 100644 index 0000000000..68594d219d --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h @@ -0,0 +1,190 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: mDNSUNP.h,v $ +Revision 1.19 2006/08/14 23:24:47 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.18 2005/04/08 21:37:57 ksekar +<rdar://problem/3792767> get_ifi_info doesn't return IPv6 interfaces on Linux + +Revision 1.17 2004/12/17 19:32:43 cheshire +Add missing semicolon + +Revision 1.16 2004/12/01 04:25:05 cheshire +<rdar://problem/3872803> Darwin patches for Solaris and Suse +Provide daemon() for platforms that don't have it + +Revision 1.15 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.14 2004/10/16 00:17:01 cheshire +<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check + +Revision 1.13 2004/03/20 05:37:09 cheshire +Fix contributed by Terry Lambert & Alfred Perlstein: +Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x + +Revision 1.12 2004/01/28 21:12:15 cheshire +Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6) + +Revision 1.11 2003/12/13 05:43:09 bradley +Fixed non-sa_len and non-IPv6 version of GET_SA_LEN macro to cast as sockaddr to access +sa_family so it works with any sockaddr-compatible address structure (e.g. sockaddr_storage). + +Revision 1.10 2003/12/11 03:03:51 rpantos +Clean up mDNSPosix so that it builds on OS X again. + +Revision 1.9 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + +Revision 1.8 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.7 2003/08/06 18:20:51 cheshire +Makefile cleanup + +Revision 1.6 2003/07/02 21:19:59 cheshire +<rdar://problem/3313413> Update copyright notices, etc., in source code comments + +Revision 1.5 2003/03/13 03:46:21 cheshire +Fixes to make the code build on Linux + +Revision 1.4 2002/12/23 22:13:32 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 06:24:35 cheshire +First checkin + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __mDNSUNP_h +#define __mDNSUNP_h + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#ifdef HAVE_LINUX +#include <linux/socket.h> +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef NOT_HAVE_SOCKLEN_T + typedef unsigned int socklen_t; +#endif + +#if !defined(_SS_MAXSIZE) +#if HAVE_IPV6 +#define sockaddr_storage sockaddr_in6 +#else +#define sockaddr_storage sockaddr +#endif // HAVE_IPV6 +#endif // !defined(_SS_MAXSIZE) + +#ifndef NOT_HAVE_SA_LEN +#define GET_SA_LEN(X) (sizeof(struct sockaddr) > ((struct sockaddr*)&(X))->sa_len ? \ + sizeof(struct sockaddr) : ((struct sockaddr*)&(X))->sa_len ) +#elif HAVE_IPV6 +#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : \ + ((struct sockaddr*)&(X))->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr)) +#else +#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr)) +#endif + +#define IFI_NAME 16 /* same as IFNAMSIZ in <net/if.h> */ +#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */ + +// Renamed from my_in_pktinfo because in_pktinfo is used by Linux. + +struct my_in_pktinfo { + struct sockaddr_storage ipi_addr; + int ipi_ifindex; /* received interface index */ + char ipi_ifname[IFI_NAME]; /* received interface name */ +}; + +/* From the text (Stevens, section 20.2): */ +/* 'As an example of recvmsg we will write a function named recvfrom_flags that */ +/* is similar to recvfrom but also returns: */ +/* 1. the returned msg_flags value, */ +/* 2. the destination addres of the received datagram (from the IP_RECVDSTADDR socket option, and */ +/* 3. the index of the interface on which the datagram was received (the IP_RECVIF socket option).' */ +extern ssize_t recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, + struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl); + +struct ifi_info { + char ifi_name[IFI_NAME]; /* interface name, null terminated */ + u_char ifi_haddr[IFI_HADDR]; /* hardware address */ + u_short ifi_hlen; /* #bytes in hardware address: 0, 6, 8 */ + short ifi_flags; /* IFF_xxx constants from <net/if.h> */ + short ifi_myflags; /* our own IFI_xxx flags */ + int ifi_index; /* interface index */ + struct sockaddr *ifi_addr; /* primary address */ + struct sockaddr *ifi_netmask; + struct sockaddr *ifi_brdaddr;/* broadcast address */ + struct sockaddr *ifi_dstaddr;/* destination address */ + struct ifi_info *ifi_next; /* next of these structures */ +}; + +#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX +#define PROC_IFINET6_PATH "/proc/net/if_inet6" +extern struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases); +#endif + +#if defined(AF_INET6) && HAVE_IPV6 +#define INET6_ADDRSTRLEN 46 /*Maximum length of IPv6 address */ +#endif + + + +#define IFI_ALIAS 1 /* ifi_addr is an alias */ + +/* From the text (Stevens, section 16.6): */ +/* 'Since many programs need to know all the interfaces on a system, we will develop a */ +/* function of our own named get_ifi_info that returns a linked list of structures, one */ +/* for each interface that is currently "up."' */ +extern struct ifi_info *get_ifi_info(int family, int doaliases); + +/* 'The free_ifi_info function, which takes a pointer that was */ +/* returned by get_ifi_info and frees all the dynamic memory.' */ +extern void free_ifi_info(struct ifi_info *); + +#ifdef NOT_HAVE_DAEMON +extern int daemon(int nochdir, int noclose); +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/multicast.xml b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/multicast.xml new file mode 100644 index 0000000000..4ef679eaaa --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/multicast.xml @@ -0,0 +1,144 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2007 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. + +#ident "%Z%%M% %I% %E% SMI" +--> + +<service_bundle type='manifest' name='SUNWdsdr:multicast'> + +<service + name='network/dns/multicast' + type='service' + version='1'> + + <dependency + name='loopback' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/loopback' /> + </dependency> + + <dependency name='net-physical' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/physical' /> + </dependency> + + <dependency + name='identity' + grouping='optional_all' + restart_on='refresh' + type='service'> + <service_fmri value='svc:/system/identity:node' /> + </dependency> + + <dependency + name='system-log' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/system-log' /> + </dependency> + + <instance name='default' enabled='false' > + + <exec_method + type='method' + name='start' + exec='/usr/lib/inet/mdnsd' + timeout_seconds='60'> + <method_context> + <method_credential user='root' group='root' /> + </method_context> + </exec_method> + + <exec_method + type='method' + name='stop' + exec=':kill' + timeout_seconds='60' /> + + <!-- to start stop mdns service --> + <property_group name='general' type='framework'> + <propval name='action_authorization' type='astring' + value='solaris.smf.manage.mdns' /> + <propval name='value_authorization' type='astring' + value='solaris.smf.manage.mdns' /> + </property_group> + + <!-- Properties in this group are used by nss_mdns module --> + <property_group name='nss_mdns_config' type='application'> + <stability value='Unstable' /> + + <!-- mDNS domain search property list. Users must + explicitly add mDNS search option values and + none are provided here by default. + <property name='search' type='astring'> + <astring_list> + <value_node value='local'/> + </astring_list> + </property> + --> + <property name='domain' type='astring'> + <astring_list> + <value_node value='local'/> + <value_node value='254.169.in-addr.arpa'/> + <value_node value='8.e.f.ip6.arpa'/> + <value_node value='9.e.f.ip6.arpa'/> + <value_node value='a.e.f.ip6.arpa'/> + <value_node value='b.e.f.ip6.arpa'/> + </astring_list> + </property> + <propval name='value_authorization' type='astring' + value='solaris.smf.value.mdns' /> + </property_group> + + </instance> + + <stability value='Unstable' /> + + <template> + + <common_name> + <loctext xml:lang='C'>DNS Service Discovery and Multicast DNS + </loctext> + </common_name> + <documentation> + <manpage title='mdnsd' section='1M' /> + <doc_link name='Service Discovery OpenSolaris Project Page' + uri="http://opensolaris.org/os/project/nwam/service-discovery/"/> + </documentation> + + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c new file mode 100644 index 0000000000..ec2bf87479 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c @@ -0,0 +1,5253 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: uDNS.c,v $ +Revision 1.230.2.1 2006/08/29 06:24:23 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.230 2006/06/29 03:02:44 cheshire +<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support + +Revision 1.229 2006/03/02 22:03:41 cheshire +<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use" +Refinement: m->rec.r.resrec.RecordType needs to be cleared *every* time around for loop, not just once at the end + +Revision 1.228 2006/02/26 00:54:42 cheshire +Fixes to avoid code generation warning/error on FreeBSD 7 + +Revision 1.227 2006/01/09 20:47:05 cheshire +<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use" + +Revision 1.226 2005/12/20 02:46:33 cheshire +<rdar://problem/4175520> mDNSPosix wide-area registration broken +Check too strict -- we can still do wide-area registration (without NAT-PMP) +without having to know our gateway address + +Revision 1.225 2005/10/21 22:51:17 cheshire +<rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code +Refinement: Shorten "check-for-broken-dns-relay" to just "dnsbugtest" +to avoid crashing NAT gateways that have a different DNS relay bug + +Revision 1.224 2005/10/20 00:10:33 cheshire +<rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code + +Revision 1.223 2005/10/17 18:52:42 cheshire +<rdar://problem/4271183> mDNSResponder crashed in CheckRecordRegistrations +Move code to unregister the service's extra records from uDNS_DeregisterService() to unlinkSRS(). + +Revision 1.222 2005/10/05 23:04:10 cheshire +Add more information to unlinkAR and startLLQHandshakeCallback error messages + +Revision 1.221 2005/10/05 17:27:48 herscher +<rdar://problem/4272516> Change 200ms delay to 10ms + +Revision 1.220 2005/09/24 01:10:09 cheshire +Fix comment typos + +Revision 1.219 2005/09/22 07:28:25 herscher +Double the delay to 200000 usec after sending out a DNS query + +Revision 1.218 2005/09/13 01:06:14 herscher +<rdar://problem/4248878> Add 100ms delay in sendQuery. + +Revision 1.217 2005/08/04 18:08:24 cheshire +Update comments + +Revision 1.216 2005/07/29 23:05:22 ksekar +<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update +Services should point to IPv6 address if IPv4 NAT mapping fails + +Revision 1.215 2005/07/29 21:01:51 ksekar +<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update +correction to original checkin - misplaced return in HostnameCallback and logic error determining v6 changes + +Revision 1.214 2005/07/29 19:46:10 ksekar +<rdar://problem/4191860> reduce polling period on failed LLQs to 15 minutes + +Revision 1.213 2005/07/29 18:04:22 ksekar +<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update + +Revision 1.212 2005/07/22 19:35:50 ksekar +<rdar://problem/4188821> SUTiger: LLQ event acknowledgments are not formated correctly + +Revision 1.211 2005/07/21 18:51:04 ksekar +<rdar://problem/4103136> mDNSResponder times out when mapping ports after sleep + +Revision 1.210 2005/07/21 18:47:31 ksekar +<rdar://problem/4137283> NAT-PMP refresh Requested Public Port should contain actual mapped port + +Revision 1.209 2005/07/04 21:16:37 cheshire +Minor code tidying -- initialize variables where they are declared + +Revision 1.208 2005/06/28 00:24:28 ksekar +<rdar://problem/4157823> memory smasher in conQueryCallback + +Revision 1.207 2005/05/13 20:45:10 ksekar +<rdar://problem/4074400> Rapid wide-area txt record updates don't work + +Revision 1.206 2005/03/31 02:19:55 cheshire +<rdar://problem/4021486> Fix build warnings +Reviewed by: Scott Herscher + +Revision 1.205 2005/03/21 00:33:51 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.204 2005/03/16 00:42:32 ksekar +<rdar://problem/4012279> Long-lived queries not working on Windows + +Revision 1.203 2005/03/04 03:00:03 ksekar +<rdar://problem/4026546> Retransmissions happen too early, causing registrations to conflict with themselves + +Revision 1.202 2005/03/01 19:29:17 ksekar +changed LogMsgs to debugfs + +Revision 1.201 2005/02/26 03:04:13 cheshire +<rdar://problem/4017292> Should not indicate successful dynamic update if no network connection +Don't try to do updates to root name server. This ensures status dot turns red if user +enters a bad host name such as just "fred" instead of a properly fully-qualified name. + +Revision 1.200 2005/02/25 17:47:45 ksekar +<rdar://problem/4021868> SendServiceRegistration fails on wake from sleep + +Revision 1.199 2005/02/25 04:21:00 cheshire +<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing + +Revision 1.198 2005/02/25 02:35:22 cheshire +<rdar://problem/4017292> Should not indicate successful dynamic update if no network connection +If we get NXDomain error looking for the _dns-update._udp record, +update status from 1 (in progress) to mStatus_NoSuchNameErr (failed) + +Revision 1.197 2005/02/24 21:56:59 ksekar +Change LogMsgs to debugfs + +Revision 1.196 2005/02/24 21:52:28 ksekar +<rdar://problem/3922768> Remove "deferred deregistration" logic for hostnames + +Revision 1.195 2005/02/22 17:53:08 ksekar +Changed successful NAT Traversals from LogMsg to LogOperation + +Revision 1.194 2005/02/15 18:38:03 ksekar +<rdar://problem/3967876> change expected/redundant log messages to debugfs. + +Revision 1.193 2005/02/15 01:17:48 ksekar +Fixed build failure. + +Revision 1.192 2005/02/14 23:01:28 ksekar +Refinement to previous checkin - don't log bad LLQ opcode if we had to send the request more than once. + +Revision 1.191 2005/02/14 18:26:51 ksekar +<rdar://problem/4005569> mDNSResponder complains about bad LLQ Opcode 2 + +Revision 1.190 2005/02/11 19:44:06 shersche +Remove extra semicolon at end of line + +Revision 1.189 2005/02/10 21:07:02 ksekar +Don't goto error in ReceiveNATAddrResponse if we receive a malformatted response + +Revision 1.188 2005/02/10 02:02:44 ksekar +Remove double semi-colon + +Revision 1.187 2005/02/09 23:28:01 ksekar +<rdar://problem/3984374> NAT-PMP response callback should return a +boolean indicating if the packet matched the request + +Revision 1.186 2005/02/04 21:56:29 ksekar +<rdar://problem/3984374> Simultaneous port map requests sometimes fail +- Refinement to previous checkin. + +Revision 1.185 2005/02/03 23:48:22 ksekar +<rdar://problem/3984374> Simultaneous port map requests sometimes fail + +Revision 1.184 2005/02/01 19:33:29 ksekar +<rdar://problem/3985239> Keychain format too restrictive + +Revision 1.183 2005/01/27 22:57:55 cheshire +Fix compile errors on gcc4 + +Revision 1.182 2005/01/25 18:55:05 ksekar +Shortened log message + +Revision 1.181 2005/01/25 02:17:32 cheshire +<rdar://problem/3971263> Don't use query ID zero in uDNS queries + +Revision 1.180 2005/01/19 21:01:54 ksekar +<rdar://problem/3955355> uDNS needs to support subtype registration and browsing + +Revision 1.179 2005/01/19 19:15:35 ksekar +Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer + +Revision 1.178 2005/01/17 23:47:58 cheshire +<rdar://problem/3904954> Wide-area services not found on little-endian + +Revision 1.177 2005/01/17 23:41:26 cheshire +Fix compile errors + +Revision 1.176 2005/01/17 21:03:04 cheshire +<rdar://problem/3904954> Wide-area services not found on little-endian + +Revision 1.175 2005/01/15 00:56:41 ksekar +<rdar://problem/3954575> Unicast services don't disappear when logging +out of VPN + +Revision 1.174 2005/01/14 18:44:28 ksekar +<rdar://problem/3954609> mDNSResponder is crashing when changing domains + +Revision 1.173 2005/01/14 18:34:22 ksekar +<rdar://problem/3954571> Services registered outside of firewall don't succeed after location change + +Revision 1.172 2005/01/11 22:50:52 ksekar +Fixed constant naming (was using kLLQ_DefLease for update leases) + +Revision 1.171 2005/01/10 04:52:49 ksekar +Changed LogMsg to debugf + +Revision 1.170 2005/01/08 00:50:05 ksekar +Fixed spelling mistake in log msg + +Revision 1.169 2005/01/08 00:42:18 ksekar +<rdar://problem/3922758> Clean up syslog messages + +Revision 1.168 2004/12/23 23:22:47 ksekar +<rdar://problem/3933606> Unicast known answers "name" pointers point to garbage stack memory + +Revision 1.167 2004/12/22 22:25:47 ksekar +<rdar://problem/3734265> NATPMP: handle location changes + +Revision 1.166 2004/12/22 00:04:12 ksekar +<rdar://problem/3930324> mDNSResponder crashing in ReceivePortMapReply + +Revision 1.165 2004/12/18 03:14:22 cheshire +DblNAT -> DoubleNAT + +Revision 1.164 2004/12/17 03:55:40 ksekar +Don't use -1 as special meaning for expiration timer (it is a valid +value, and is redundant with our state variables) + +Revision 1.163 2004/12/17 03:51:53 ksekar +<rdar://problem/3920991> Don't update TXT record if service registration fails + +Revision 1.162 2004/12/17 01:29:11 ksekar +<rdar://problem/3920598> Questions can go deaf on location changes + +Revision 1.161 2004/12/16 20:42:02 cheshire +Fix compiler warnings + +Revision 1.160 2004/12/16 20:13:00 cheshire +<rdar://problem/3324626> Cache memory management improvements + +Revision 1.159 2004/12/15 02:11:22 ksekar +<rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness + +Revision 1.158 2004/12/15 02:04:28 ksekar +Refinement to previous checkin - we should still return NatTraversal error when the port mapping fails + +Revision 1.157 2004/12/15 01:39:21 ksekar +Refinement to previous checkin - we should still return NatTraversal error when the port mapping fails + +Revision 1.156 2004/12/15 01:18:57 ksekar +<rdar://problem/3825979> Call DeregisterService on nat port map failure + +Revision 1.155 2004/12/14 21:21:20 ksekar +<rdar://problem/3825979> NAT-PMP: Update response format to contain "Seconds Since Boot" + +Revision 1.154 2004/12/14 20:52:27 cheshire +Add question->qnamehash and cr->resrec.namehash to log message + +Revision 1.153 2004/12/14 20:45:02 cheshire +Improved error logging in "unexpected answer" message + +Revision 1.152 2004/12/14 03:02:10 ksekar +<rdar://problem/3919016> Rare race condition can cause crash + +Revision 1.151 2004/12/13 21:45:08 ksekar +uDNS_DeregisterService should return NoError if called twice (to follow mDNS behavior expected by daemon layer) + +Revision 1.150 2004/12/13 20:42:41 ksekar +Fixed LogMsg + +Revision 1.149 2004/12/13 18:10:03 ksekar +Fixed LogMsg + +Revision 1.148 2004/12/13 01:18:04 ksekar +Fixed unused variable warning for non-debug builds + +Revision 1.147 2004/12/12 23:51:42 ksekar +<rdar://problem/3845683> Wide-area registrations should fallback to using DHCP hostname as target + +Revision 1.146 2004/12/12 23:30:40 ksekar +<rdar://problem/3916987> Extra RRs not properly unlinked when parent service registration fails + +Revision 1.145 2004/12/12 22:56:29 ksekar +<rdar://problem/3668508> Need to properly handle duplicate long-lived queries + +Revision 1.144 2004/12/11 20:55:29 ksekar +<rdar://problem/3916479> Clean up registration state machines + +Revision 1.143 2004/12/10 01:21:27 cheshire +<rdar://problem/3914089> Get rid of "LLQ Responses over TCP not currently supported" message + +Revision 1.142 2004/12/08 02:03:31 ksekar +<rdar://problem/3865124> Looping on NAT Traversal error - check for +NULL RR on error + +Revision 1.141 2004/12/07 01:39:28 cheshire +Don't fail if the same server is responsible for more than one domain +(e.g. the same DNS server may be responsible for both apple.com. and 17.in-addr.arpa.) + +Revision 1.140 2004/12/06 21:15:22 ksekar +<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.139 2004/12/06 19:08:03 cheshire +Add clarifying comment -- CountLabels() excludes the final root label. + +Revision 1.138 2004/12/06 01:45:54 ksekar +Correct wording in LogMsg + +Revision 1.137 2004/12/03 20:40:35 ksekar +<rdar://problem/3865124> Looping on NAT Traversal error + +Revision 1.136 2004/12/03 07:20:50 ksekar +<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails + +Revision 1.135 2004/12/03 05:18:33 ksekar +<rdar://problem/3810596> mDNSResponder needs to return more specific TSIG errors + +Revision 1.134 2004/12/02 20:03:49 ksekar +<rdar://problem/3889647> Still publishes wide-area domains even after switching to a local subnet + +Revision 1.133 2004/12/02 18:37:52 ksekar +<rdar://problem/3758233> Registering with port number zero should not create a port mapping + +Revision 1.132 2004/12/01 20:57:19 ksekar +<rdar://problem/3873921> Wide Area Service Discovery must be split-DNS aware + +Revision 1.131 2004/12/01 19:59:27 cheshire +<rdar://problem/3882643> Crash in mDNSPlatformTCPConnect +If a TCP response has the TC bit set, don't respond by just trying another TCP connection + +Revision 1.130 2004/12/01 02:43:23 cheshire +Don't call StatusCallback if function pointer is null + +Revision 1.129 2004/11/30 23:51:06 cheshire +Remove double semicolons + +Revision 1.128 2004/11/25 01:48:30 ksekar +<rdar://problem/3878991> Logging into VPN does not trigger registration of address record + +Revision 1.127 2004/11/25 01:41:36 ksekar +Changed unnecessary LogMsgs to debugfs + +Revision 1.126 2004/11/23 23:54:17 ksekar +<rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures +can crash mDNSResponder + +Revision 1.125 2004/11/23 04:16:48 cheshire +Removed receiveMsg() routine. + +Revision 1.124 2004/11/23 04:06:51 cheshire +Get rid of floating point constant -- in a small embedded device, bringing in all +the floating point libraries just to halve an integer value is a bit too heavyweight. + +Revision 1.123 2004/11/22 17:16:20 ksekar +<rdar://problem/3854298> Unicast services don't disappear when you disable all networking + +Revision 1.122 2004/11/19 18:00:34 ksekar +<rdar://problem/3682646> Security: use random ID for one-shot unicast queries + +Revision 1.121 2004/11/19 04:24:08 ksekar +<rdar://problem/3682609> Security: Enforce a "window" on one-shot wide-area queries + +Revision 1.120 2004/11/19 02:32:43 ksekar +<rdar://problem/3682608> Wide-Area Security: Add LLQ-ID to events + +Revision 1.119 2004/11/18 23:21:24 ksekar +<rdar://problem/3764544> LLQ Security: Need to verify src port/address for LLQ handshake + +Revision 1.118 2004/11/18 22:58:37 ksekar +Removed old comment. + +Revision 1.117 2004/11/18 18:04:21 ksekar +Restore checkins lost due to repository disk failure: Update comments & <rdar://problem/3880688> + +Revision 1.xxx 2004/11/17 06:17:57 cheshire +Update comments to show correct SRV names: _dns-update._udp.<zone>. and _dns-llq._udp.<zone>. + +Revision 1.xxx 2004/11/17 00:45:28 ksekar +<rdar://problem/3880688> Result of putUpdateLease not error-checked + +Revision 1.116 2004/11/16 01:41:47 ksekar +Fixed typo in debugf + +Revision 1.115 2004/11/15 20:09:24 ksekar +<rdar://problem/3719050> Wide Area support for Add/Remove record + +Revision 1.114 2004/11/13 02:32:47 ksekar +<rdar://problem/3868216> LLQ mobility fragile on non-primary interface +- fixed incorrect state comparison in CheckQueries + +Revision 1.113 2004/11/13 02:29:52 ksekar +<rdar://problem/3878386> LLQ refreshes not reliable + +Revision 1.112 2004/11/11 20:45:14 ksekar +<rdar://problem/3876052> self-conflict test not compatible with some BIND servers + +Revision 1.111 2004/11/11 20:14:55 ksekar +<rdar://problem/3719574> Wide-Area registrations not deregistered on sleep + +Revision 1.110 2004/11/10 23:53:53 ksekar +Remove no longer relevant comment + +Revision 1.109 2004/11/10 20:40:53 ksekar +<rdar://problem/3868216> LLQ mobility fragile on non-primary interface + +Revision 1.108 2004/11/01 20:36:16 ksekar +<rdar://problem/3802395> mDNSResponder should not receive Keychain Notifications + +Revision 1.107 2004/10/26 06:11:41 cheshire +Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed + +Revision 1.106 2004/10/26 03:52:03 cheshire +Update checkin comments + +Revision 1.105 2004/10/26 01:15:06 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.104 2004/10/25 21:41:38 ksekar +<rdar://problem/3852958> wide-area name conflicts can cause crash + +Revision 1.103 2004/10/25 19:30:52 ksekar +<rdar://problem/3827956> Simplify dynamic host name structures + +Revision 1.102 2004/10/23 01:16:00 cheshire +<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts + +Revision 1.101 2004/10/22 20:52:07 ksekar +<rdar://problem/3799260> Create NAT port mappings for Long Lived Queries + +Revision 1.100 2004/10/20 02:16:41 cheshire +Improve "could not confirm existence of NS record" error message +Don't call newRR->RecordCallback if it is NULL + +Revision 1.99 2004/10/19 21:33:18 cheshire +<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.98 2004/10/16 00:16:59 cheshire +<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check + +Revision 1.97 2004/10/15 23:00:18 ksekar +<rdar://problem/3799242> Need to update LLQs on location changes + +Revision 1.96 2004/10/12 23:30:44 ksekar +<rdar://problem/3609944> mDNSResponder needs to follow CNAME referrals + +Revision 1.95 2004/10/12 03:15:09 ksekar +<rdar://problem/3835612> mDNS_StartQuery shouldn't return transient no-server error + +Revision 1.94 2004/10/12 02:49:20 ksekar +<rdar://problem/3831228> Clean up LLQ sleep/wake, error handling + +Revision 1.93 2004/10/08 04:17:25 ksekar +<rdar://problem/3831819> Don't use DNS extensions if the server does not advertise required SRV record + +Revision 1.92 2004/10/08 03:54:35 ksekar +<rdar://problem/3831802> Refine unicast polling intervals + +Revision 1.91 2004/09/30 17:45:34 ksekar +<rdar://problem/3821119> lots of log messages: mDNS_SetPrimaryIP: IP address unchanged + +Revision 1.90 2004/09/25 00:22:13 ksekar +<rdar://problem/3815534> Crash in uDNS_RegisterService + +Revision 1.89 2004/09/24 19:14:53 cheshire +Remove unused "extern mDNS mDNSStorage" + +Revision 1.88 2004/09/23 20:48:15 ksekar +Clarify retransmission debugf messages. + +Revision 1.87 2004/09/22 00:41:59 cheshire +Move tcp connection status codes into the legal range allocated for mDNS use + +Revision 1.86 2004/09/21 23:40:11 ksekar +<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure + +Revision 1.85 2004/09/21 22:38:27 ksekar +<rdar://problem/3810286> PrimaryIP type uninitialized + +Revision 1.84 2004/09/18 00:30:39 cheshire +<rdar://problem/3806643> Infinite loop in CheckServiceRegistrations + +Revision 1.83 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.82 2004/09/16 21:36:36 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.81 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.80 2004/09/16 01:58:21 cheshire +Fix compiler warnings + +Revision 1.79 2004/09/16 00:24:48 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.78 2004/09/15 01:16:57 ksekar +<rdar://problem/3797598> mDNSResponder printing too many messages + +Revision 1.77 2004/09/14 23:27:47 cheshire +Fix compile errors + +Revision 1.76 2004/09/14 22:22:00 ksekar +<rdar://problem/3800920> Legacy browses broken against some BIND versions + +Revision 1.75 2004/09/03 19:23:05 ksekar +<rdar://problem/3788460>: Need retransmission mechanism for wide-area service registrations + +Revision 1.74 2004/09/02 17:49:04 ksekar +<rdar://problem/3785135>: 8A246: mDNSResponder crash while logging on restart +Fixed incorrect conversions, changed %s to %##s for all domain names. + +Revision 1.73 2004/09/02 01:39:40 cheshire +For better readability, follow consistent convention that QR bit comes first, followed by OP bits + +Revision 1.72 2004/09/01 03:59:29 ksekar +<rdar://problem/3783453>: Conditionally compile out uDNS code on Windows + +Revision 1.71 2004/08/27 17:51:53 ksekar +Replaced unnecessary LogMsg with debugf. + +Revision 1.70 2004/08/25 00:37:27 ksekar +<rdar://problem/3774635>: Cleanup DynDNS hostname registration code + +Revision 1.69 2004/08/18 17:35:41 ksekar +<rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.68 2004/08/14 03:22:41 cheshire +<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.67 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.66 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.65 2004/08/13 23:12:32 cheshire +Don't use strcpy() and strlen() on "struct domainname" objects; +use AssignDomainName() and DomainNameLength() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + +Revision 1.64 2004/08/13 23:01:05 cheshire +Use platform-independent mDNSNULL instead of NULL + +Revision 1.63 2004/08/12 00:32:36 ksekar +<rdar://problem/3759567>: LLQ Refreshes never terminate if unanswered + +Revision 1.62 2004/08/10 23:19:14 ksekar +<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.61 2004/07/30 17:40:06 ksekar +<rdar://problem/3739115>: TXT Record updates not available for wide-area services + +Revision 1.60 2004/07/29 19:40:05 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.59 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.58 2004/07/27 07:35:38 shersche +fix syntax error, variables declared in the middle of a block + +Revision 1.57 2004/07/26 22:49:30 ksekar +<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client + +Revision 1.56 2004/07/26 19:14:44 ksekar +<rdar://problem/3737814>: 8A210: mDNSResponder crashed in startLLQHandshakeCallback + +Revision 1.55 2004/07/15 19:01:33 ksekar +<rdar://problem/3681029>: Check for incorrect time comparisons + +Revision 1.54 2004/06/22 02:10:53 ksekar +<rdar://problem/3705433>: Lighthouse failure causes packet flood to DNS + +Revision 1.53 2004/06/17 20:49:09 ksekar +<rdar://problem/3690436>: mDNSResponder crash while location cycling + +Revision 1.52 2004/06/17 01:13:11 ksekar +<rdar://problem/3696616>: polling interval too short + +Revision 1.51 2004/06/10 04:36:44 cheshire +Fix compiler warning + +Revision 1.50 2004/06/10 00:55:13 ksekar +<rdar://problem/3686213>: crash on network reconnect + +Revision 1.49 2004/06/10 00:10:50 ksekar +<rdar://problem/3686174>: Infinite Loop in uDNS_Execute() + +Revision 1.48 2004/06/09 20:03:37 ksekar +<rdar://problem/3686163>: Incorrect copying of resource record in deregistration + +Revision 1.47 2004/06/09 03:48:28 ksekar +<rdar://problem/3685226>: nameserver address fails with prod. Lighthouse server + +Revision 1.46 2004/06/09 01:44:30 ksekar +<rdar://problem/3681378> reworked Cache Record copy code + +Revision 1.45 2004/06/08 18:54:47 ksekar +<rdar://problem/3681378>: mDNSResponder leaks after exploring in Printer Setup Utility + +Revision 1.44 2004/06/05 00:33:51 cheshire +<rdar://problem/3681029>: Check for incorrect time comparisons + +Revision 1.43 2004/06/05 00:14:44 cheshire +Fix signed/unsigned and other compiler warnings + +Revision 1.42 2004/06/04 22:36:16 ksekar +Properly set u->nextevent in uDNS_Execute + +Revision 1.41 2004/06/04 08:58:29 ksekar +<rdar://problem/3668624>: Keychain integration for secure dynamic update + +Revision 1.40 2004/06/03 03:09:58 ksekar +<rdar://problem/3668626>: Garbage Collection for Dynamic Updates + +Revision 1.39 2004/06/01 23:46:50 ksekar +<rdar://problem/3675149>: DynDNS: dynamically look up LLQ/Update ports + +Revision 1.38 2004/05/31 22:19:44 ksekar +<rdar://problem/3258021>: Feature: DNS server->client notification on +record changes (#7805) - revert to polling mode on setup errors + +Revision 1.37 2004/05/28 23:42:37 ksekar +<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.36 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.35 2004/05/07 23:01:04 ksekar +Cleaned up list traversal in deriveGoodbyes - removed unnecessary +conditional assignment. + +Revision 1.34 2004/05/05 18:26:12 ksekar +Periodically re-transmit questions if the send() fails. Include +internal questions in retransmission. + +Revision 1.33 2004/05/05 17:40:06 ksekar +Removed prerequisite from deregistration update - it does not work for +shared records, and is unnecessary until we have more sophisticated +name conflict management. + +Revision 1.32 2004/05/05 17:32:18 ksekar +Prevent registration of loopback interface caused by removal of +Multicast flag in interface structure. + +Revision 1.31 2004/05/05 17:05:02 ksekar +Use LargeCacheRecord structs when pulling records off packets + +Revision 1.30 2004/04/16 21:33:27 ksekar +Fixed bug in processing GetZoneData responses that do not use BIND formatting. + +Revision 1.29 2004/04/15 20:03:13 ksekar +Clarified log message when pulling bad resource records off packet. + +Revision 1.28 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.27 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.26 2004/04/14 19:36:05 ksekar +Fixed memory corruption error in deriveGoodbyes. + +Revision 1.25 2004/04/14 04:07:11 ksekar +Fixed crash in IsActiveUnicastQuery(). Removed redundant checks in routine. + +Revision 1.24 2004/04/08 09:41:40 bradley +Added const to AuthRecord in deadvertiseIfCallback to match callback typedef. + +Revision 1.23 2004/03/24 00:29:45 ksekar +Make it safe to call StopQuery in a unicast question callback + +Revision 1.22 2004/03/19 10:11:09 bradley +Added AuthRecord * cast from umalloc for C++ builds. + +Revision 1.21 2004/03/15 02:03:45 bradley +Added const to params where needed to match prototypes. Changed SetNewRData calls to use 0 instead +of -1 for unused size to fix warning. Disable assignment within conditional warnings with Visual C++. + +Revision 1.20 2004/03/13 02:07:26 ksekar +<rdar://problem/3192546>: DynDNS: Dynamic update of service records + +Revision 1.19 2004/03/13 01:57:33 ksekar +<rdar://problem/3192546>: DynDNS: Dynamic update of service records + +Revision 1.18 2004/02/21 08:34:15 bradley +Added casts from void * to specific type for C++ builds. Changed void * l-value cast +r-value cast to fix problems with VC++ builds. Removed empty switch to fix VC++ error. + +Revision 1.17 2004/02/21 02:06:24 cheshire +Can't use anonymous unions -- they're non-standard and don't work on all compilers + +Revision 1.16 2004/02/12 01:51:45 cheshire +Don't try to send uDNS queries unless we have at least one uDNS server available + +Revision 1.15 2004/02/10 03:02:46 cheshire +Fix compiler warning + +Revision 1.14 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.13 2004/02/03 22:15:01 ksekar +Fixed nameToAddr error check: don't abort state machine on nxdomain error. + +Revision 1.12 2004/02/03 19:47:36 ksekar +Added an asynchronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.11 2004/01/30 02:12:30 ksekar +Changed uDNS_ReceiveMsg() to correctly return void. + +Revision 1.10 2004/01/29 02:59:17 ksekar +Unicast DNS: Changed from a resource record oriented question/response +matching to packet based matching. New callback architecture allows +collections of records in a response to be processed differently +depending on the nature of the request, and allows the same structure +to be used for internal and client-driven queries with different processing needs. + +Revision 1.9 2004/01/28 20:20:45 ksekar +Unified ActiveQueries and ActiveInternalQueries lists, using a flag to +demux them. Check-in includes work-in-progress code, #ifdef'd out. + +Revision 1.8 2004/01/28 02:30:07 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.7 2004/01/27 20:15:22 cheshire +<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53 + +Revision 1.6 2004/01/24 23:47:17 cheshire +Use mDNSOpaque16fromIntVal() instead of shifting and masking + +Revision 1.5 2004/01/24 04:59:15 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.4 2004/01/24 04:19:26 cheshire +Restore overwritten checkin 1.2 + +Revision 1.3 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.2 2004/01/22 03:48:41 cheshire +Make sure uDNS client doesn't accidentally use query ID zero + +Revision 1.1 2003/12/13 03:05:27 ksekar +<rdar://problem/3192548>: DynDNS: Unicast query of service records + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "uDNS.h" + +#if(defined(_MSC_VER)) + // Disable "assignment within conditional expression". + // Other compilers understand the convention that if you place the assignment expression within an extra pair + // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. + // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal + // to the compiler that the assignment is intentional, we have to just turn this warning off completely. + #pragma warning(disable:4706) +#endif + +#define umalloc(x) mDNSPlatformMemAllocate(x) // short hands for common routines +#define ufree(x) mDNSPlatformMemFree(x) +#define ubzero(x,y) mDNSPlatformMemZero(x,y) +#define umemcpy(x, y, l) mDNSPlatformMemCopy(y, x, l) // uses memcpy(2) arg ordering + +// Asynchronous operation types + +typedef enum + { + zoneDataResult + // other async. operation names go here + } AsyncOpResultType; + +typedef struct + { + domainname zoneName; + mDNSAddr primaryAddr; + mDNSu16 zoneClass; + mDNSIPPort llqPort; + mDNSIPPort updatePort; + } zoneData_t; + +// other async. result struct defs go here + +typedef struct + { + AsyncOpResultType type; + zoneData_t zoneData; + // other async result structs go here + } AsyncOpResult; + +typedef void AsyncOpCallback(mStatus err, mDNS *const m, void *info, const AsyncOpResult *result); + + +// Private Function Prototypes +// Note: In general, functions are ordered such that they do not require forward declarations. +// However, prototypes are used where cyclic call graphs exist (e.g. foo calls bar, and bar calls +// foo), or when they aid in the grouping or readability of code (e.g. state machine code that is easier +// read top-to-bottom.) + +mDNSlocal mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n); +mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m); +mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort, AsyncOpCallback callback, void *callbackInfo); +mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID); +mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr); +mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs); +mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs); +mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result); +mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive); +mDNSlocal void RestartQueries(mDNS *m); +mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer); +mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context); + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Temporary workaround +#endif + +// 17 Places in this file directly call mDNSPlatformTimeNow(), which is unsafe +// The platform function is now called mDNSPlatformRawTime(), and +// mDNSPlatformTimeNow() is defined here as a temporary workaround. +// This is a gross hack, and after this change has been tested for a while, +// all these calls should be replaced by simple references to m->timenow + +mDNSlocal mDNSs32 mDNSPlatformTimeNow(mDNS *m) + { + if (m->mDNS_busy && m->timenow) return(m->timenow); + LogMsg("ERROR: uDNS.c code executing without holding main mDNS lock"); + + // To get a quick and easy stack trace to find out *how* this routine + // is being called without holding main mDNS lock, uncomment the line below: + // *(long*)0=0; + + return(mDNS_TimeNow(m)); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - General Utility Functions +#endif + +// CountLabels() returns number of labels in name, excluding final root label +// (e.g. for "apple.com." CountLabels returns 2.) +mDNSlocal int CountLabels(const domainname *d) + { + int count = 0; + const mDNSu8 *ptr; + + for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++; + return count; + } + +mDNSlocal mDNSOpaque16 newMessageID(uDNS_GlobalInfo *u) + { + static mDNSBool randomized = mDNSfalse; + + if (!randomized) { u->NextMessageID = (mDNSu16)mDNSRandom(0xFFFF); randomized = mDNStrue; } + if (u->NextMessageID == 0) u->NextMessageID++; + return mDNSOpaque16fromIntVal(u->NextMessageID++); + } + +// unlink an AuthRecord from a linked list +mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr) + { + while (*list && *list != rr) list = &(*list)->next; + if (*list) { *list = rr->next; rr->next = mDNSNULL; return(mStatus_NoError); } + LogMsg("ERROR: unlinkAR - no such active record %##s", rr->resrec.name->c); + return(mStatus_NoSuchRecord); + } + +mDNSlocal void unlinkSRS(mDNS *m, ServiceRecordSet *srs) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + ServiceRecordSet **p; + NATTraversalInfo *n = u->NATTraversals; + + // verify that no NAT objects reference this service + while (n) + { + if (n->reg.ServiceRegistration == srs) + { + NATTraversalInfo *tmp = n; + n = n->next; + LogMsg("ERROR: Unlinking service record set %##s still referenced by NAT traversal object!", srs->RR_SRV.resrec.name->c); + FreeNATInfo(m, tmp); + } + else n = n->next; + } + + for (p = &u->ServiceRegistrations; *p; p = &(*p)->next) + if (*p == srs) + { + ExtraResourceRecord *e; + *p = srs->next; + srs->next = mDNSNULL; + for (e=srs->Extras; e; e=e->next) + if (unlinkAR(&u->RecordRegistrations, &e->r)) + LogMsg("unlinkSRS: extra record %##s not found", e->r.resrec.name->c); + return; + } + LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list %##s", srs->RR_SRV.resrec.name->c); + } + +mDNSlocal void LinkActiveQuestion(uDNS_GlobalInfo *u, DNSQuestion *q) + { + if (uDNS_IsActiveQuery(q, u)) + { LogMsg("LinkActiveQuestion - %##s (%d) already in list!", q->qname.c, q->qtype); return; } + + q->next = u->ActiveQueries; + u->ActiveQueries = q; + } + +// set retry timestamp for record with exponential backoff +// (for service record sets, use RR_SRV as representative for time checks +mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mStatus SendErr) + { + rr->LastAPTime = mDNSPlatformTimeNow(m); + if (SendErr == mStatus_TransientErr || rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL) rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL; + else if (rr->ThisAPInterval*2 <= MAX_UCAST_POLL_INTERVAL) rr->ThisAPInterval *= 2; + else if (rr->ThisAPInterval != MAX_UCAST_POLL_INTERVAL) rr->ThisAPInterval = MAX_UCAST_POLL_INTERVAL; + } + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Name Server List Management +#endif + +mDNSexport void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *addr, const domainname *d) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSServer *s, **p = &u->Servers; + + mDNS_Lock(m); + if (!d) d = (domainname *)""; + + while (*p) // Check if we already have this {server,domain} pair registered + { + if (mDNSSameAddress(&(*p)->addr, addr) && SameDomainName(&(*p)->domain, d)) + LogMsg("Note: DNS Server %#a for domain %##s registered more than once", addr, d->c); + p=&(*p)->next; + } + + // allocate, add to list + s = umalloc(sizeof(*s)); + if (!s) { LogMsg("Error: mDNS_AddDNSServer - malloc"); goto end; } + s->addr = *addr; + s->del = mDNSfalse; + s->teststate = DNSServer_Untested; + AssignDomainName(&s->domain, d); + s->next = mDNSNULL; + *p = s; + + end: + mDNS_Unlock(m); + } + +mDNSexport void mDNS_DeleteDNSServers(mDNS *const m) + { + DNSServer *s; + mDNS_Lock(m); + + s = m->uDNS_info.Servers; + m->uDNS_info.Servers = mDNSNULL; + while (s) + { + DNSServer *tmp = s; + s = s->next; + ufree(tmp); + } + + mDNS_Unlock(m); + } + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - authorization management +#endif + +mDNSlocal uDNS_AuthInfo *GetAuthInfoForName(const uDNS_GlobalInfo *u, const domainname *name) + { + uDNS_AuthInfo *ptr; + while (name->c[0]) + { + for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->zone, name)) return(ptr); + name = (const domainname *)(name->c + 1 + name->c[0]); + } + return mDNSNULL; + } + +mDNSlocal void DeleteAuthInfoForZone(uDNS_GlobalInfo *u, const domainname *zone) + { + uDNS_AuthInfo *ptr, *prev = mDNSNULL; + + for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->zone, zone)) + { + if (prev) prev->next = ptr->next; + else u->AuthInfoList = ptr->next; + ufree(ptr); + return; + } + prev = ptr; + } + } + +mDNSexport mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const char *sharedSecret) + { + uDNS_AuthInfo *info; + mDNSu8 keybuf[1024]; + mDNSs32 keylen; + uDNS_GlobalInfo *u = &m->uDNS_info; + mStatus status = mStatus_NoError; + + mDNS_Lock(m); + + if (GetAuthInfoForName(u, zone)) DeleteAuthInfoForZone(u, zone); + if (!key) goto exit; + + info = (uDNS_AuthInfo*)umalloc(sizeof(*info)); + if (!info) { LogMsg("ERROR: umalloc"); status = mStatus_NoMemoryErr; goto exit; } + ubzero(info, sizeof(*info)); + AssignDomainName(&info->zone, zone); + AssignDomainName(&info->keyname, key); + + keylen = DNSDigest_Base64ToBin(sharedSecret, keybuf, 1024); + if (keylen < 0) + { + LogMsg("ERROR: mDNS_SetSecretForZone - could not convert shared secret %s from base64", sharedSecret); + ufree(info); + status = mStatus_UnknownErr; + goto exit; + } + DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen); + + // link into list + info->next = m->uDNS_info.AuthInfoList; + m->uDNS_info.AuthInfoList = info; +exit: + mDNS_Unlock(m); + return status; + } + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - NAT Traversal +#endif + +mDNSlocal mDNSBool DomainContainsLabelString(const domainname *d, const char *str) + { + const domainlabel *l; + domainlabel buf; + + if (!MakeDomainLabelFromLiteralString(&buf, str)) return mDNSfalse; + + for (l = (const domainlabel *)d; l->c[0]; l = (const domainlabel *)(l->c + l->c[0]+1)) + if (SameDomainLabel(l->c, buf.c)) return mDNStrue; + return mDNSfalse; + } + +// allocate struct, link into global list, initialize +mDNSlocal NATTraversalInfo *AllocNATInfo(mDNS *const m, NATOp_t op, NATResponseHndlr callback) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *info = umalloc(sizeof(NATTraversalInfo)); + if (!info) { LogMsg("ERROR: malloc"); return mDNSNULL; } + ubzero(info, sizeof(NATTraversalInfo)); + info->next = u->NATTraversals; + u->NATTraversals = info; + info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY; + info->op = op; + info->state = NATState_Init; + info->ReceiveResponse = callback; + info->PublicPort.NotAnInteger = 0; + info->Router = u->Router; + return info; + } + +// unlink from list, deallocate +mDNSlocal mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n) + { + NATTraversalInfo *ptr, *prev = mDNSNULL; + ServiceRecordSet *s = m->uDNS_info.ServiceRegistrations; + + // Verify that object is not referenced by any services + while (s) + { + if (s->uDNS_info.NATinfo == n) + { + LogMsg("Error: Freeing NAT info object still referenced by Service Record Set %##s!", s->RR_SRV.resrec.name->c); + s->uDNS_info.NATinfo = mDNSNULL; + } + s = s->next; + } + + if (n == m->uDNS_info.LLQNatInfo) m->uDNS_info.LLQNatInfo = mDNSNULL; + ptr = m->uDNS_info.NATTraversals; + while (ptr) + { + if (ptr == n) + { + if (prev) prev->next = ptr->next; + else m->uDNS_info.NATTraversals = ptr->next; + ufree(n); + return mDNStrue; + } + prev = ptr; + ptr = ptr->next; + } + LogMsg("FreeNATInfo: NATTraversalInfo not found in list"); + return mDNSfalse; + } + +mDNSlocal void SendNATMsg(NATTraversalInfo *info, mDNS *m) + { + mStatus err; + uDNS_GlobalInfo *u = &m->uDNS_info; + + if (info->state != NATState_Request && info->state != NATState_Refresh) + { LogMsg("SendNATMsg: Bad state %d", info->state); return; } + + if (u->Router.ip.v4.NotAnInteger) + { + // send msg if we have a router + const mDNSu8 *end = (mDNSu8 *)&info->request; + if (info->op == NATOp_AddrRequest) end += sizeof(NATAddrRequest); + else end += sizeof(NATPortMapRequest); + + err = mDNSPlatformSendUDP(m, &info->request, end, 0, &u->Router, NATPMPPort); + if (!err) (info->ntries++); // don't increment attempt counter if the send failed + } + + // set retry + if (info->RetryInterval < NATMAP_INIT_RETRY) info->RetryInterval = NATMAP_INIT_RETRY; + else if (info->RetryInterval * 2 > NATMAP_MAX_RETRY) info->RetryInterval = NATMAP_MAX_RETRY; + else info->RetryInterval *= 2; + info->retry = mDNSPlatformTimeNow(m) + info->RetryInterval; + } + +mDNSlocal mDNSBool ReceiveNATAddrResponse(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len) + { + mStatus err = mStatus_NoError; + AuthRecord *rr = mDNSNULL; + NATAddrReply *response = (NATAddrReply *)pkt; + mDNSAddr addr; + + if (n->state != NATState_Request) + { + LogMsg("ReceiveNATAddrResponse: bad state %d", n->state); + return mDNSfalse; + } + + rr = n->reg.RecordRegistration; + if (!rr) + { + LogMsg("ReceiveNATAddrResponse: registration cancelled"); + return mDNSfalse; + } + + addr.type = mDNSAddrType_IPv4; + addr.ip.v4 = rr->resrec.rdata->u.ipv4; + + if (!pkt) // timeout + { +#ifdef _LEGACY_NAT_TRAVERSAL_ + err = LNT_GetPublicIP(&addr.ip.v4); + if (err) goto end; + else n->state = NATState_Legacy; +#else + debugf("ReceiveNATAddrResponse: timeout"); + err = mStatus_NATTraversal; + goto end; +#endif // _LEGACY_NAT_TRAVERSAL_ + } + else + { + if (len < sizeof(*response)) + { + LogMsg("ReceiveNATAddrResponse: response too short (%d bytes)", len); + return mDNSfalse; + } + if (response->vers != NATMAP_VERS) + { + LogMsg("ReceiveNATAddrResponse: received version %d (expect version %d)", pkt[0], NATMAP_VERS); + return mDNSfalse; + } + if (response->opcode != (NATOp_AddrRequest | NATMAP_RESPONSE_MASK)) + { + LogMsg("ReceiveNATAddrResponse: bad response code %d", response->opcode); + return mDNSfalse; + } + if (response->err.NotAnInteger) + { LogMsg("ReceiveAddrResponse: received error %d", mDNSVal16(response->err)); err = mStatus_NATTraversal; goto end; } + + addr.ip.v4 = response->PubAddr; + n->state = NATState_Established; + } + + if (IsPrivateV4Addr(&addr)) + { + LogMsg("ReceiveNATAddrResponse: Double NAT"); + err = mStatus_DoubleNAT; + goto end; + } + + end: + if (err) + { + FreeNATInfo(m, n); + if (rr) + { + rr->uDNS_info.NATinfo = mDNSNULL; + rr->uDNS_info.state = regState_Unregistered; // note that rr is not yet in global list + rr->RecordCallback(m, rr, mStatus_NATTraversal); + // note - unsafe to touch rr after callback + } + return mDNStrue; + } + else LogOperation("Received public IP address %d.%d.%d.%d from NAT.", addr.ip.v4.b[0], addr.ip.v4.b[1], addr.ip.v4.b[2], addr.ip.v4.b[3]); + rr->resrec.rdata->u.ipv4 = addr.ip.v4; // replace rdata w/ public address + uDNS_RegisterRecord(m, rr); + return mDNStrue; + } + + +mDNSlocal void StartGetPublicAddr(mDNS *m, AuthRecord *AddressRec) + { + NATAddrRequest *req; + uDNS_GlobalInfo *u = &m->uDNS_info; + + NATTraversalInfo *info = AllocNATInfo(m, NATOp_AddrRequest, ReceiveNATAddrResponse); + if (!info) { uDNS_RegisterRecord(m, AddressRec); return; } + AddressRec->uDNS_info.NATinfo = info; + info->reg.RecordRegistration = AddressRec; + info->state = NATState_Request; + + // format message + req = &info->request.AddrReq; + req->vers = NATMAP_VERS; + req->opcode = NATOp_AddrRequest; + + if (!u->Router.ip.v4.NotAnInteger) + { + debugf("No router. Will retry NAT traversal in %ld ticks", NATMAP_INIT_RETRY); + return; + } + + SendNATMsg(info, m); + } + + +mDNSlocal void RefreshNATMapping(NATTraversalInfo *n, mDNS *m) + { + n->state = NATState_Refresh; + n->RetryInterval = NATMAP_INIT_RETRY; + n->ntries = 0; + SendNATMsg(n, m); + } + +mDNSlocal void LLQNatMapComplete(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + LLQ_Info *llqInfo; + NATTraversalInfo *n = u->LLQNatInfo; + + if (!n) { LogMsg("Error: LLQNatMapComplete called with NULL LLQNatInfo"); return; } + if (n->state != NATState_Established && n->state != NATState_Legacy && n->state != NATState_Error) + { LogMsg("LLQNatMapComplete - bad nat state %d", n->state); return; } + + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) + { + DNSQuestion *q = u->CurrentQuery; + u->CurrentQuery = u->CurrentQuery->next; + llqInfo = q->uDNS_info.llq; + if (q->LongLived && llqInfo->state == LLQ_NatMapWait) + { + if (n->state == NATState_Error) + { + llqInfo->NATMap = mDNSfalse; + llqInfo->question->uDNS_info.responseCallback = llqResponseHndlr; + llqInfo->state = LLQ_Poll; + llqInfo->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + llqInfo->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + } + else { llqInfo->state = LLQ_GetZoneInfo; startLLQHandshake(m, llqInfo, mDNSfalse); } + } + } + } + +mDNSlocal mDNSBool ReceivePortMapReply(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len) + { + ServiceRecordSet *srs = n->reg.ServiceRegistration; + mDNSIPPort priv = srs ? srs->RR_SRV.resrec.rdata->u.srv.port : m->UnicastPort4; + mDNSu32 lease; + mDNSBool deletion = !n->request.PortReq.lease.NotAnInteger; + NATPortMapReply *reply = (NATPortMapReply *)pkt; + mDNSu8 *service = srs ? srs->RR_SRV.resrec.name->c : (mDNSu8 *)"\016LLQ event port"; + + if (n->state != NATState_Request && n->state != NATState_Refresh) + { LogMsg("ReceivePortMapReply (%##s): bad state %d", service, n->state); return mDNSfalse; } + + if (!pkt && !deletion) // timeout + { +#ifdef _LEGACY_NAT_TRAVERSAL_ + mDNSIPPort pub; + int ntries = 0; + mStatus err; + mDNSBool tcp = (srs && DomainContainsLabelString(srs->RR_PTR.resrec.name, "_tcp")); + + pub = priv; // initially request priv == pub + while (1) + { + err = LNT_MapPort(priv, pub, tcp); + if (!err) + { + n->PublicPort = pub; + n->state = NATState_Legacy; + goto end; + } + else if (err != mStatus_AlreadyRegistered || ++ntries > LEGACY_NATMAP_MAX_TRIES) + { + n->state = NATState_Error; + goto end; + } + else + { + // the mapping we want is taken - try a random port + mDNSu16 RandPort = mDNSRandom(DYN_PORT_MAX - DYN_PORT_MIN) + DYN_PORT_MIN; + pub = mDNSOpaque16fromIntVal(RandPort); + } + } +#else + goto end; +#endif // _LEGACY_NAT_TRAVERSAL_ + } + + if (len < sizeof(*reply)) { LogMsg("ReceivePortMapReply: response too short (%d bytes)", len); return mDNSfalse; } + if (reply->vers != NATMAP_VERS) { LogMsg("ReceivePortMapReply: received version %d (expect version %d)", pkt[0], NATMAP_VERS); return mDNSfalse; } + if (reply->opcode != (n->op | NATMAP_RESPONSE_MASK)) { LogMsg("ReceivePortMapReply: bad response code %d", pkt[1]); return mDNSfalse; } + if (reply->err.NotAnInteger) { LogMsg("ReceivePortMapReply: received error %d", mDNSVal16(reply->err)); return mDNSfalse; } + if (priv.NotAnInteger != reply->priv.NotAnInteger) return mDNSfalse; // packet does not match this request + + if (!srs && n != m->uDNS_info.LLQNatInfo) + { + LogMsg("ReceivePortMapReply: registration cancelled"); //!!!KRS change to debugf before checkin + FreeNATInfo(m, n); + return mDNStrue; + } + + if (deletion) { n->state = NATState_Deleted; return mDNStrue; } + + lease = (mDNSu32)mDNSVal32(reply->lease); + if (lease > 0x70000000UL / mDNSPlatformOneSecond) lease = 0x70000000UL / mDNSPlatformOneSecond; + + if (n->state == NATState_Refresh && reply->pub.NotAnInteger != n->PublicPort.NotAnInteger) + LogMsg("ReceivePortMapReply: NAT refresh changed public port from %d to %d", mDNSVal16(n->PublicPort), mDNSVal16(reply->pub)); + // this should never happen + // !!!KRS to be defensive, use SRVChanged flag on service and deregister here + + n->PublicPort = reply->pub; + if (reply->pub.NotAnInteger != n->request.PortReq.pub.NotAnInteger) n->request.PortReq.pub = reply->pub; // set message buffer for refreshes + + n->retry = mDNSPlatformTimeNow(m) + ((mDNSs32)lease * mDNSPlatformOneSecond / 2); // retry half way to expiration + + if (n->state == NATState_Refresh) { n->state = NATState_Established; return mDNStrue; } + n->state = NATState_Established; + + end: + if (n->state != NATState_Established && n->state != NATState_Legacy) + { + LogMsg("NAT Port Mapping (%##s): timeout", service); + if (pkt) LogMsg("!!! timeout with non-null packet"); + n->state = NATState_Error; + if (srs) + { + uDNS_HostnameInfo *hi = m->uDNS_info.Hostnames; + while (hi) + { + if (hi->arv6 && (hi->arv6->uDNS_info.state == regState_Registered || hi->arv6->uDNS_info.state == regState_Refresh)) break; + else hi = hi->next; + } + + if (hi) + { + debugf("Port map failed for service %##s - using IPv6 service target", service); + srs->uDNS_info.NATinfo = mDNSNULL; + FreeNATInfo(m, n); + goto register_service; + } + else srs->uDNS_info.state = regState_NATError; + } + else LLQNatMapComplete(m); + return mDNStrue; + } + else LogOperation("Mapped private port %d to public port %d", mDNSVal16(priv), mDNSVal16(n->PublicPort)); + + if (!srs) { LLQNatMapComplete(m); return mDNStrue; } + + register_service: + if (srs->uDNS_info.ns.ip.v4.NotAnInteger) SendServiceRegistration(m, srs); // non-zero server address means we already have necessary zone data to send update + else + { + srs->uDNS_info.state = regState_FetchingZoneData; + startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + return mDNStrue; + } + +mDNSlocal void FormatPortMaprequest(NATTraversalInfo *info, mDNSIPPort port) + { + NATPortMapRequest *req = &info->request.PortReq; + + req->vers = NATMAP_VERS; + req->opcode = info->op; + req->unused.NotAnInteger = 0; + req->priv = port; + req->pub = port; + req->lease = mDNSOpaque32fromIntVal(NATMAP_DEFAULT_LEASE); + } + +mDNSlocal void SendInitialPMapReq(mDNS *m, NATTraversalInfo *info) + { + if (!m->uDNS_info.Router.ip.v4.NotAnInteger) + { + debugf("No router. Will retry NAT traversal in %ld seconds", NATMAP_INIT_RETRY); + info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY; + info->RetryInterval = NATMAP_INIT_RETRY; + return; + } + SendNATMsg(info, m); + return; + } + +mDNSlocal void StartNATPortMap(mDNS *m, ServiceRecordSet *srs) + { + NATOp_t op; + NATTraversalInfo *info; + + if (DomainContainsLabelString(srs->RR_PTR.resrec.name, "_tcp")) op = NATOp_MapTCP; + else if (DomainContainsLabelString(srs->RR_PTR.resrec.name, "_udp")) op = NATOp_MapUDP; + else { LogMsg("StartNATPortMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name->c); goto error; } + + if (srs->uDNS_info.NATinfo) { LogMsg("Error: StartNATPortMap - NAT info already initialized!"); FreeNATInfo(m, srs->uDNS_info.NATinfo); } + info = AllocNATInfo(m, op, ReceivePortMapReply); + srs->uDNS_info.NATinfo = info; + info->reg.ServiceRegistration = srs; + info->state = NATState_Request; + + FormatPortMaprequest(info, srs->RR_SRV.resrec.rdata->u.srv.port); + SendInitialPMapReq(m, info); + return; + + error: + startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + +mDNSlocal void DeleteNATPortMapping(mDNS *m, NATTraversalInfo *nat, ServiceRecordSet *srs) + { + if (nat->state == NATState_Established) // let other edge-case states expire for simplicity + { + // zero lease + nat->request.PortReq.lease.NotAnInteger = 0; + nat->state = NATState_Request; + SendNATMsg(nat, m); + } +#ifdef _LEGACY_NAT_TRAVERSAL_ + else if (nat->state == NATState_Legacy) + { + mStatus err = mStatus_NoError; + mDNSBool tcp = srs ? DomainContainsLabelString(srs->RR_PTR.resrec.name, "_tcp") : mDNSfalse; + err = LNT_UnmapPort(nat->PublicPort, tcp); + if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %ld", err); + } +#else + (void)srs; // unused +#endif // _LEGACY_NAT_TRAVERSAL_ + } + +mDNSlocal void StartLLQNatMap(mDNS *m) + { + NATTraversalInfo *info = AllocNATInfo(m, NATOp_MapUDP, ReceivePortMapReply); + uDNS_GlobalInfo *u = &m->uDNS_info; + + u->LLQNatInfo = info; + + info->reg.RecordRegistration = mDNSNULL; + info->reg.ServiceRegistration = mDNSNULL; + info->state = NATState_Request; + FormatPortMaprequest(info, m->UnicastPort4); + SendInitialPMapReq(m, info); + return; + } + +// if LLQ NAT context unreferenced, delete the mapping +mDNSlocal void CheckForUnreferencedLLQMapping(mDNS *m) + { + NATTraversalInfo *nat = m->uDNS_info.LLQNatInfo; + DNSQuestion *q; + + if (!nat) return; + + for (q = m->uDNS_info.ActiveQueries; q; q = q->next) + if (q->LongLived && q->uDNS_info.llq->NATMap) return; + + //to avoid race condition if we need to recreate before this finishes, we do one-shot deregistration + if (nat->state == NATState_Established || nat->state == NATState_Legacy) + DeleteNATPortMapping(m, nat, mDNSNULL); // for simplicity we allow other states to expire + FreeNATInfo(m, nat); // note: this clears the global LLQNatInfo pointer + } + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - host name and interface management +#endif + +// if we ever want to refine support for multiple hostnames, we can add logic matching service names to a particular hostname +// for now, we grab the first registered DynDNS name, if any, or a static name we learned via a reverse-map query +mDNSlocal mDNSBool GetServiceTarget(uDNS_GlobalInfo *u, AuthRecord *srv, domainname *dst) + { + uDNS_HostnameInfo *hi = u->Hostnames; + (void)srv; // unused + + dst->c[0] = 0; + while (hi) + { + if (hi->arv4 && (hi->arv4->uDNS_info.state == regState_Registered || hi->arv4->uDNS_info.state == regState_Refresh)) + { + AssignDomainName(dst, hi->arv4->resrec.name); + return mDNStrue; + } + if (hi->arv6 && (hi->arv6->uDNS_info.state == regState_Registered || hi->arv6->uDNS_info.state == regState_Refresh)) + { + AssignDomainName(dst, hi->arv4->resrec.name); + return mDNStrue; + } + hi = hi->next; + } + + if (u->StaticHostname.c[0]) { AssignDomainName(dst, &u->StaticHostname); return mDNStrue; } + return mDNSfalse; + } + +mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + ExtraResourceRecord *e; + + // Target change if: + // We have a target and were previously waiting for one, or + // We had a target and no longer do, or + // The target has changed + + domainname newtarget; + domainname *curtarget = &srs->RR_SRV.resrec.rdata->u.srv.target; + mDNSBool HaveTarget = GetServiceTarget(u, &srs->RR_SRV, &newtarget); + mDNSBool TargetChanged = (HaveTarget && srs->uDNS_info.state == regState_NoTarget) || (curtarget->c[0] && !HaveTarget) || !SameDomainName(curtarget, &newtarget); + mDNSBool HaveZoneData = srs->uDNS_info.ns.ip.v4.NotAnInteger ? mDNStrue : mDNSfalse; + + // Nat state change if: + // We were behind a NAT, and now we are behind a new NAT, or + // We're not behind a NAT but our port was previously mapped to a different public port + // We were not behind a NAT and now we are + + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + mDNSIPPort port = srs->RR_SRV.resrec.rdata->u.srv.port; + mDNSBool NATChanged = mDNSfalse; + mDNSBool NowBehindNAT = port.NotAnInteger && IsPrivateV4Addr(&u->AdvertisedV4); + mDNSBool WereBehindNAT = nat != mDNSNULL; + mDNSBool NATRouterChanged = nat && nat->Router.ip.v4.NotAnInteger != u->Router.ip.v4.NotAnInteger; + mDNSBool PortWasMapped = nat && (nat->state == NATState_Established || nat->state == NATState_Legacy) && nat->PublicPort.NotAnInteger != port.NotAnInteger; + + if (WereBehindNAT && NowBehindNAT && NATRouterChanged) NATChanged = mDNStrue; + else if (!NowBehindNAT && PortWasMapped) NATChanged = mDNStrue; + else if (!WereBehindNAT && NowBehindNAT) NATChanged = mDNStrue; + + if (!TargetChanged && !NATChanged) return; + + debugf("UpdateSRV (%##s) HadZoneData=%d, TargetChanged=%d, HaveTarget=%d, NowBehindNAT=%d, WereBehindNAT=%d, NATRouterChanged=%d, PortWasMapped=%d", + srs->RR_SRV.resrec.name->c, HaveZoneData, TargetChanged, HaveTarget, NowBehindNAT, WereBehindNAT, NATRouterChanged, PortWasMapped); + + switch(srs->uDNS_info.state) + { + case regState_FetchingZoneData: + case regState_Cancelled: + case regState_DeregPending: + case regState_DeregDeferred: + case regState_Unregistered: + case regState_NATMap: + case regState_ExtraQueued: + // In these states, the SRV has either not yet been registered (it will get up-to-date information when it is) + // or is in the process of, or has already been, deregistered + return; + + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // let the in-flight operation complete before updating + srs->uDNS_info.SRVUpdateDeferred = mDNStrue; + return; + + case regState_NATError: + if (!NATChanged) return; + // if nat changed, register if we have a target (below) + + case regState_NoTarget: + if (HaveTarget) + { + debugf("UpdateSRV: %s service %##s", HaveZoneData ? (NATChanged && NowBehindNAT ? "Starting Port Map for" : "Registering") : "Getting Zone Data for", srs->RR_SRV.resrec.name->c); + if (!HaveZoneData) + { + srs->uDNS_info.state = regState_FetchingZoneData; + startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + else + { + if (nat && (NATChanged || !NowBehindNAT)) { srs->uDNS_info.NATinfo = mDNSNULL; FreeNATInfo(m, nat); } + if (NATChanged && NowBehindNAT) { srs->uDNS_info.state = regState_NATMap; StartNATPortMap(m, srs); } + else SendServiceRegistration(m, srs); + } + } + return; + + case regState_Registered: + // target or nat changed. deregister service. upon completion, we'll look for a new target + debugf("UpdateSRV: SRV record changed for service %##s - deregistering (will re-register with new SRV)", srs->RR_SRV.resrec.name->c); + for (e = srs->Extras; e; e = e->next) e->r.uDNS_info.state = regState_ExtraQueued; // extra will be re-registed if the service is re-registered + srs->uDNS_info.SRVChanged = mDNStrue; + SendServiceDeregistration(m, srs); + return; + } + } + +mDNSlocal void UpdateSRVRecords(mDNS *m) + { + ServiceRecordSet *srs; + + for (srs = m->uDNS_info.ServiceRegistrations; srs; srs = srs->next) UpdateSRV(m, srs); + } + +mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + uDNS_HostnameInfo *hi = (uDNS_HostnameInfo *)rr->RecordContext; + + if (result == mStatus_MemFree) + { + if (hi) + { + if (hi->arv4 == rr) hi->arv4 = mDNSNULL; + else if (hi->arv4 == rr) hi->arv6 = mDNSNULL; + rr->RecordContext = mDNSNULL; + if (!hi->arv4 && !hi->arv6) ufree(hi); // free hi when both v4 and v6 AuthRecs deallocated + } + ufree(rr); + return; + } + + if (result) + { + // don't unlink or free - we can retry when we get a new address/router + if (rr->resrec.rrtype == kDNSType_A) + LogMsg("HostnameCallback: Error %ld for registration of %##s IP %.4a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv4); + else + LogMsg("HostnameCallback: Error %ld for registration of %##s IP %.16a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv6); + if (!hi) { ufree(rr); return; } + if (rr->uDNS_info.state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!"); + + if ((!hi->arv4 || hi->arv4->uDNS_info.state == regState_Unregistered) && + (!hi->arv6 || hi->arv6->uDNS_info.state == regState_Unregistered)) + { + // only deliver status if both v4 and v6 fail + rr->RecordContext = (void *)hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; + } + return; + } + // register any pending services that require a target + UpdateSRVRecords(m); + + // Deliver success to client + if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; } + if (rr->resrec.rrtype == kDNSType_A) + LogMsg("Registered hostname %##s IP %.4a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv4); + else + LogMsg("Registered hostname %##s IP %.16a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv6); + + rr->RecordContext = (void *)hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; + } + +// register record or begin NAT traversal +mDNSlocal void AdvertiseHostname(mDNS *m, uDNS_HostnameInfo *h) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + + if (u->AdvertisedV4.ip.v4.NotAnInteger && h->arv4->uDNS_info.state == regState_Unregistered) + { + if (IsPrivateV4Addr(&u->AdvertisedV4)) + StartGetPublicAddr(m, h->arv4); + else + { + LogMsg("Advertising %##s IP %.4a", h->arv4->resrec.name->c, &u->AdvertisedV4.ip.v4); + uDNS_RegisterRecord(m, h->arv4); + } + } + if (u->AdvertisedV6.ip.v6.b[0] && h->arv6->uDNS_info.state == regState_Unregistered) + { + LogMsg("Advertising %##s IP %.16a", h->arv4->resrec.name->c, &u->AdvertisedV6.ip.v6); + uDNS_RegisterRecord(m, h->arv6); + } + } + +mDNSlocal void FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + const domainname *pktname = &answer->rdata->u.name; + domainname *storedname = &m->uDNS_info.StaticHostname; + uDNS_HostnameInfo *h = m->uDNS_info.Hostnames; + + (void)question; + + debugf("FoundStaticHostname: %##s -> %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "added" : "removed"); + if (AddRecord && !SameDomainName(pktname, storedname)) + { + AssignDomainName(storedname, pktname); + while (h) + { + if ((h->arv4 && (h->arv4->uDNS_info.state == regState_FetchingZoneData || h->arv4->uDNS_info.state == regState_Pending || h->arv4->uDNS_info.state == regState_NATMap)) || + (h->arv6 && (h->arv6->uDNS_info.state == regState_FetchingZoneData || h->arv6->uDNS_info.state == regState_Pending))) + { + // if we're in the process of registering a dynamic hostname, delay SRV update so we don't have to reregister services if the dynamic name succeeds + m->uDNS_info.DelaySRVUpdate = mDNStrue; + m->uDNS_info.NextSRVUpdate = mDNSPlatformTimeNow(m) + (5 * mDNSPlatformOneSecond); + return; + } + h = h->next; + } + UpdateSRVRecords(m); + } + else if (!AddRecord && SameDomainName(pktname, storedname)) + { + storedname->c[0] = 0; + UpdateSRVRecords(m); + } + } + +mDNSlocal void GetStaticHostname(mDNS *m) + { + char buf[MAX_ESCAPED_DOMAIN_NAME]; + DNSQuestion *q = &m->uDNS_info.ReverseMap; + mDNSu8 *ip = m->uDNS_info.AdvertisedV4.ip.v4.b; + mStatus err; + + if (m->uDNS_info.ReverseMapActive) + { + uDNS_StopQuery(m, q); + m->uDNS_info.ReverseMapActive = mDNSfalse; + } + + m->uDNS_info.StaticHostname.c[0] = 0; + if (!m->uDNS_info.AdvertisedV4.ip.v4.NotAnInteger) return; + ubzero(q, sizeof(*q)); + mDNS_snprintf(buf, MAX_ESCAPED_DOMAIN_NAME, "%d.%d.%d.%d.in-addr.arpa.", ip[3], ip[2], ip[1], ip[0]); + if (!MakeDomainNameFromDNSNameString(&q->qname, buf)) { LogMsg("Error: GetStaticHostname - bad name %s", buf); return; } + + q->InterfaceID = mDNSInterface_Any; + q->Target = zeroAddr; + q->qtype = kDNSType_PTR; + q->qclass = kDNSClass_IN; + q->LongLived = mDNSfalse; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = mDNSfalse; + q->QuestionCallback = FoundStaticHostname; + q->QuestionContext = mDNSNULL; + + err = uDNS_StartQuery(m, q); + if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err); + else m->uDNS_info.ReverseMapActive = mDNStrue; + } + +mDNSlocal void AssignHostnameInfoAuthRecord(mDNS *m, uDNS_HostnameInfo *hi, int type) + { + AuthRecord **dst = (type == mDNSAddrType_IPv4 ? &hi->arv4 : &hi->arv6); + AuthRecord *ar = umalloc(sizeof(*ar)); + uDNS_GlobalInfo *u = &m->uDNS_info; + + if (type != mDNSAddrType_IPv4 && type != mDNSAddrType_IPv6) { LogMsg("ERROR: AssignHostnameInfoAuthRecord - bad type %d", type); return; } + if (!ar) { LogMsg("ERROR: AssignHostnameInfoAuthRecord - malloc"); return; } + + mDNS_SetupResourceRecord(ar, mDNSNULL, 0, type == mDNSAddrType_IPv4 ? kDNSType_A : kDNSType_AAAA, 1, kDNSRecordTypeKnownUnique, HostnameCallback, hi); + AssignDomainName(ar->resrec.name, &hi->fqdn); + + // only set RData if we have a valid IP + if (type == mDNSAddrType_IPv4 && u->AdvertisedV4.ip.v4.NotAnInteger) + { + if (u->MappedV4.ip.v4.NotAnInteger) ar->resrec.rdata->u.ipv4 = u->MappedV4.ip.v4; + else ar->resrec.rdata->u.ipv4 = u->AdvertisedV4.ip.v4; + } + else if (type == mDNSAddrType_IPv6 && u->AdvertisedV6.ip.v6.b[0]) + { + ar->resrec.rdata->u.ipv6 = u->AdvertisedV6.ip.v6; + } + + ar->uDNS_info.state = regState_Unregistered; + + if (*dst) + { + LogMsg("ERROR: AssignHostnameInfoAuthRecord - overwriting %s AuthRec", type == mDNSAddrType_IPv4 ? "IPv4" : "IPv6"); + unlinkAR(&u->RecordRegistrations, *dst); + (*dst)->RecordContext = mDNSNULL; // defensively clear backpointer to avoid doubly-referenced context + } + + *dst = ar; + } + + +// Deregister hostnames and register new names for each host domain with the current global +// values for the hostlabel and primary IP address +mDNSlocal void UpdateHostnameRegistrations(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + uDNS_HostnameInfo *i; + + for (i = u->Hostnames; i; i = i->next) + { + if (i->arv4 && i->arv4->uDNS_info.state != regState_Unregistered && + i->arv4->resrec.rdata->u.ipv4.NotAnInteger != u->AdvertisedV4.ip.v4.NotAnInteger && + i->arv4->resrec.rdata->u.ipv4.NotAnInteger !=u->MappedV4.ip.v4.NotAnInteger) + { + uDNS_DeregisterRecord(m, i->arv4); + i->arv4 = mDNSNULL; + } + if (i->arv6 && !mDNSPlatformMemSame(i->arv6->resrec.rdata->u.ipv6.b, u->AdvertisedV6.ip.v6.b, 16) && i->arv6->uDNS_info.state != regState_Unregistered) + { + uDNS_DeregisterRecord(m, i->arv6); + i->arv6 = mDNSNULL; + } + + if (!i->arv4 && u->AdvertisedV4.ip.v4.NotAnInteger) AssignHostnameInfoAuthRecord(m, i, mDNSAddrType_IPv4); + else if (i->arv4 && i->arv4->uDNS_info.state == regState_Unregistered) i->arv4->resrec.rdata->u.ipv4 = u->AdvertisedV4.ip.v4; // simply overwrite unregistered + if (!i->arv6 && u->AdvertisedV6.ip.v6.b[0]) AssignHostnameInfoAuthRecord(m, i, mDNSAddrType_IPv6); + else if (i->arv6 &&i->arv6->uDNS_info.state == regState_Unregistered) i->arv6->resrec.rdata->u.ipv6 = u->AdvertisedV6.ip.v6; + + AdvertiseHostname(m, i); + } + } + +mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + uDNS_HostnameInfo *ptr, *new; + + mDNS_Lock(m); + + // check if domain already registered + for (ptr = u->Hostnames; ptr; ptr = ptr->next) + { + if (SameDomainName(fqdn, &ptr->fqdn)) + { LogMsg("Host Domain %##s already in list", fqdn->c); goto exit; } + } + + // allocate and format new address record + new = umalloc(sizeof(*new)); + if (!new) { LogMsg("ERROR: mDNS_AddDynDNSHostname - malloc"); goto exit; } + ubzero(new, sizeof(*new)); + new->next = u->Hostnames; + u->Hostnames = new; + + AssignDomainName(&new->fqdn, fqdn); + new->StatusCallback = StatusCallback; + new->StatusContext = StatusContext; + + if (u->AdvertisedV4.ip.v4.NotAnInteger) AssignHostnameInfoAuthRecord(m, new, mDNSAddrType_IPv4); + else new->arv4 = mDNSNULL; + if (u->AdvertisedV6.ip.v6.b[0]) AssignHostnameInfoAuthRecord(m, new, mDNSAddrType_IPv6); + else new->arv6 = mDNSNULL; + + if (u->AdvertisedV6.ip.v6.b[0] || u->AdvertisedV4.ip.v4.NotAnInteger) AdvertiseHostname(m, new); + +exit: + mDNS_Unlock(m); + } + +mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + uDNS_HostnameInfo **ptr = &u->Hostnames; + + mDNS_Lock(m); + + while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next; + if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c); + else + { + uDNS_HostnameInfo *hi = *ptr; + *ptr = (*ptr)->next; // unlink + if (hi->arv4) + { + hi->arv4->RecordContext = mDNSNULL; // about to free wrapper struct + if (hi->arv4->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->arv4); + else { ufree(hi->arv4); hi->arv4 = mDNSNULL; } + } + if (hi->arv6) + { + hi->arv6->RecordContext = mDNSNULL; // about to free wrapper struct + if (hi->arv6->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->arv6); + else { ufree(hi->arv6); hi->arv6 = mDNSNULL; } + } + ufree(hi); + } + UpdateSRVRecords(m); + mDNS_Unlock(m); + } + +mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSBool v4Changed, v6Changed, RouterChanged; + + if (v4addr && v4addr->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo V4 address - incorrect type. Discarding."); return; } + if (v6addr && v6addr->type != mDNSAddrType_IPv6) { LogMsg("mDNS_SetPrimaryInterfaceInfo V6 address - incorrect type. Discarding."); return; } + if (router && router->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 router. Discarding."); return; } + + mDNS_Lock(m); + + v4Changed = (v4addr ? v4addr->ip.v4.NotAnInteger : 0) != u->AdvertisedV4.ip.v4.NotAnInteger; + v6Changed = v6addr ? !mDNSPlatformMemSame(v6addr, &u->AdvertisedV6, sizeof(*v6addr)) : (u->AdvertisedV6.ip.v6.b[0] != 0); + RouterChanged = (router ? router->ip.v4.NotAnInteger : 0) != u->Router.ip.v4.NotAnInteger; + +#if MDNS_DEBUGMSGS + if (v4addr && (v4Changed || RouterChanged)) + LogMsg("mDNS_SetPrimaryInterfaceInfo: address changed from %d.%d.%d.%d to %d.%d.%d.%d:%d", + u->AdvertisedV4.ip.v4.b[0], u->AdvertisedV4.ip.v4.b[1], u->AdvertisedV4.ip.v4.b[2], u->AdvertisedV4.ip.v4.b[3], + v4addr->ip.v4.b[0], v4addr->ip.v4.b[1], v4addr->ip.v4.b[2], v4addr->ip.v4.b[3]); +#endif // MDNS_DEBUGMSGS + + if ((v4Changed || RouterChanged) && u->MappedV4.ip.v4.NotAnInteger) u->MappedV4.ip.v4.NotAnInteger = 0; + if (v4addr) u->AdvertisedV4 = *v4addr; else u->AdvertisedV4.ip.v4.NotAnInteger = 0; + if (v6addr) u->AdvertisedV6 = *v6addr; else ubzero(u->AdvertisedV6.ip.v6.b, 16); + if (router) u->Router = *router; else u->Router.ip.v4.NotAnInteger = 0; + // setting router to zero indicates that nat mappings must be reestablished when router is reset + + if ((v4Changed || RouterChanged || v6Changed) && v4addr) + { + // don't update these unless we've got V4 + UpdateHostnameRegistrations(m); + UpdateSRVRecords(m); + GetStaticHostname(m); // look up reverse map record to find any static hostnames for our IP address + } + + mDNS_Unlock(m); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Incoming Message Processing +#endif + +mDNSlocal mDNSBool kaListContainsAnswer(DNSQuestion *question, CacheRecord *rr) + { + CacheRecord *ptr; + + for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next) + if (SameResourceRecord(&ptr->resrec, &rr->resrec)) return mDNStrue; + + return mDNSfalse; + } + + +mDNSlocal void removeKnownAnswer(DNSQuestion *question, CacheRecord *rr) + { + CacheRecord *ptr, *prev = mDNSNULL; + + for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next) + { + if (SameResourceRecord(&ptr->resrec, &rr->resrec)) + { + if (prev) prev->next = ptr->next; + else question->uDNS_info.knownAnswers = ptr->next; + ufree(ptr); + return; + } + prev = ptr; + } + LogMsg("removeKnownAnswer() called for record not in KA list"); + } + + +mDNSlocal void addKnownAnswer(DNSQuestion *question, const CacheRecord *rr) + { + CacheRecord *newCR = mDNSNULL; + mDNSu32 size; + + size = sizeof(CacheRecord) + rr->resrec.rdlength - InlineCacheRDSize; + newCR = (CacheRecord *)umalloc(size); + if (!newCR) { LogMsg("ERROR: addKnownAnswer - malloc"); return; } + umemcpy(newCR, rr, size); + newCR->resrec.rdata = (RData*)&newCR->rdatastorage; + newCR->resrec.rdata->MaxRDLength = rr->resrec.rdlength; + newCR->resrec.name = &question->qname; + newCR->next = question->uDNS_info.knownAnswers; + question->uDNS_info.knownAnswers = newCR; + } + +mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question) + { + const mDNSu8 *ptr; + int i; + CacheRecord *fptr, *ka, *cr, *answers = mDNSNULL, *prev = mDNSNULL; + LargeCacheRecord *lcr; + + if (question != m->uDNS_info.CurrentQuery) { LogMsg("ERROR: deriveGoodbyes called without CurrentQuery set!"); return; } + + ptr = LocateAnswers(msg, end); + if (!ptr) goto pkt_error; + + if (!msg->h.numAnswers) + { + // delete the whole KA list + ka = question->uDNS_info.knownAnswers; + while (ka) + { + debugf("deriving goodbye for %##s", ka->resrec.name->c); + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + question->QuestionCallback(m, question, &ka->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + // CAUTION: Need to be careful after calling question->QuestionCallback(), + // because the client's callback function is allowed to do anything, + // including starting/stopping queries, registering/deregistering records, etc. + if (question != m->uDNS_info.CurrentQuery) + { + debugf("deriveGoodbyes - question removed via callback. returning."); + return; + } + fptr = ka; + ka = ka->next; + ufree(fptr); + } + question->uDNS_info.knownAnswers = mDNSNULL; + return; + } + + // make a list of all the new answers + for (i = 0; i < msg->h.numAnswers; i++) + { + lcr = (LargeCacheRecord *)umalloc(sizeof(LargeCacheRecord)); + if (!lcr) goto malloc_error; + ubzero(lcr, sizeof(LargeCacheRecord)); + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAns, lcr); + if (!ptr) goto pkt_error; + cr = &lcr->r; + if (ResourceRecordAnswersQuestion(&cr->resrec, question)) + { + cr->next = answers; + answers = cr; + } + else ufree(cr); + } + + // make sure every known answer is in the answer list + ka = question->uDNS_info.knownAnswers; + while (ka) + { + for (cr = answers; cr; cr = cr->next) + { if (SameResourceRecord(&ka->resrec, &cr->resrec)) break; } + if (!cr) + { + // record is in KA list but not answer list - remove from KA list + if (prev) prev->next = ka->next; + else question->uDNS_info.knownAnswers = ka->next; + debugf("deriving goodbye for %##s", ka->resrec.name->c); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + question->QuestionCallback(m, question, &ka->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + // CAUTION: Need to be careful after calling question->QuestionCallback(), + // because the client's callback function is allowed to do anything, + // including starting/stopping queries, registering/deregistering records, etc. + if (question != m->uDNS_info.CurrentQuery) + { + debugf("deriveGoodbyes - question removed via callback. returning."); + return; + } + fptr = ka; + ka = ka->next; + ufree(fptr); + } + else + { + prev = ka; + ka = ka->next; + } + } + + // free temp answers list + cr = answers; + while (cr) { fptr = cr; cr = cr->next; ufree(fptr); } + + return; + + pkt_error: + LogMsg("ERROR: deriveGoodbyes - received malformed response to query for %##s (%d)", + question->qname.c, question->qtype); + return; + + malloc_error: + LogMsg("ERROR: Malloc"); + } + +mDNSlocal void pktResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, mDNSBool llq) + { + const mDNSu8 *ptr; + int i; + CacheRecord *cr = &m->rec.r; + mDNSBool goodbye, inKAList; + int followedCNames = 0; + static const int maxCNames = 5; + LLQ_Info *llqInfo = question->uDNS_info.llq; + domainname origname; + origname.c[0] = 0; + + if (question != m->uDNS_info.CurrentQuery) + { LogMsg("ERROR: pktResponseHdnlr called without CurrentQuery ptr set!"); return; } + + if (question->uDNS_info.Answered == 0 && msg->h.numAnswers == 0 && !llq) + { + /* NXDOMAIN error or empty RR set - notify client */ + question->uDNS_info.Answered = mDNStrue; + + /* Create empty resource record */ + cr->resrec.RecordType = kDNSRecordTypeUnregistered; + cr->resrec.InterfaceID = mDNSNULL; + cr->resrec.name = &question->qname; + cr->resrec.rrtype = question->qtype; + cr->resrec.rrclass = question->qclass; + cr->resrec.rroriginalttl = 1; /* What should we use for the TTL? TTL from SOA for domain? */ + cr->resrec.rdlength = 0; + cr->resrec.rdestimate = 0; + cr->resrec.namehash = 0; + cr->resrec.namehash = 0; + cr->resrec.rdata = (RData*)&cr->rdatastorage; + cr->resrec.rdata->MaxRDLength = cr->resrec.rdlength; + + /* Pass empty answer to callback */ + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + question->QuestionCallback(m, question, &cr->resrec, 0); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + // CAUTION: Need to be careful after calling question->QuestionCallback(), + // because the client's callback function is allowed to do anything, + // including starting/stopping queries, registering/deregistering records, etc. + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + if (question != m->uDNS_info.CurrentQuery) + { + debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning."); + return; + } + } + + question->uDNS_info.Answered = mDNStrue; + + ptr = LocateAnswers(msg, end); + if (!ptr) goto pkt_error; + + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &m->rec); + if (!ptr) goto pkt_error; + if (ResourceRecordAnswersQuestion(&cr->resrec, question)) + { + goodbye = llq ? ((mDNSs32)cr->resrec.rroriginalttl == -1) : mDNSfalse; + if (cr->resrec.rrtype == kDNSType_CNAME) + { + if (followedCNames > (maxCNames - 1)) LogMsg("Error: too many CNAME referals for question %##s", &origname); + else + { + debugf("Following cname %##s -> %##s", question->qname.c, cr->resrec.rdata->u.name.c); + if (question->ReturnCNAME) + { + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + question->QuestionCallback(m, question, &cr->resrec, !goodbye); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + // CAUTION: Need to be careful after calling question->QuestionCallback(), + // because the client's callback function is allowed to do anything, + // including starting/stopping queries, registering/deregistering records, etc. + if (question != m->uDNS_info.CurrentQuery) + { + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning."); + return; + } + } + AssignDomainName(&origname, &question->qname); + AssignDomainName(&question->qname, &cr->resrec.rdata->u.name); + question->qnamehash = DomainNameHashValue(&question->qname); + followedCNames++; + i = -1; // restart packet answer matching + ptr = LocateAnswers(msg, end); + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + continue; + } + } + + inKAList = kaListContainsAnswer(question, cr); + + if ((goodbye && !inKAList) || (!goodbye && inKAList)) + { + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + continue; // list up to date + } + if (!inKAList) addKnownAnswer(question, cr); + if (goodbye) removeKnownAnswer(question, cr); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + question->QuestionCallback(m, question, &cr->resrec, !goodbye); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + if (question != m->uDNS_info.CurrentQuery) + { + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning"); + return; + } + } + + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + + if (!llq || llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume) + { + deriveGoodbyes(m, msg, end,question); + if (llq && llqInfo->deriveRemovesOnResume) llqInfo->deriveRemovesOnResume = mDNSfalse; + } + + // Our interval may be set lower to recover from failures -- now that we have an answer, fully back off retry. + // If the server advertised an LLQ-specific port number then that implies that this zone + // *wants* to support LLQs, so if the setup fails (e.g. because we are behind a NAT) + // then we use a slightly faster polling rate to give slightly better user experience. + if (llq && llqInfo->state == LLQ_Poll && llqInfo->servPort.NotAnInteger) question->ThisQInterval = LLQ_POLL_INTERVAL; + else if (question->ThisQInterval < MAX_UCAST_POLL_INTERVAL) question->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + return; + + pkt_error: + LogMsg("ERROR: pktResponseHndlr - received malformed response to query for %##s (%d)", + question->qname.c, question->qtype); + return; + } + +mDNSlocal void simpleResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context) + { + (void)context; // unused + pktResponseHndlr(m, msg, end, question, mDNSfalse); + } + +mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context) + { + (void)context; // unused + pktResponseHndlr(m, msg, end, question, mDNStrue); + } + +mDNSlocal mStatus ParseTSIGError(mDNS *m, const DNSMessage *msg, const mDNSu8 *end, const domainname *displayname) + { + LargeCacheRecord lcr; + const mDNSu8 *ptr; + mStatus err = mStatus_NoError; + int i; + + ptr = LocateAdditionals(msg, end); + if (!ptr) goto finish; + + for (i = 0; i < msg->h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); + if (!ptr) goto finish; + if (lcr.r.resrec.rrtype == kDNSType_TSIG) + { + mDNSu32 macsize; + mDNSu8 *rd = lcr.r.resrec.rdata->u.data; + mDNSu8 *rdend = rd + MaximumRDSize; + int alglen = DomainNameLength(&lcr.r.resrec.rdata->u.name); + + if (rd + alglen > rdend) goto finish; + rd += alglen; // algorithm name + if (rd + 6 > rdend) goto finish; + rd += 6; // 48-bit timestamp + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + rd += sizeof(mDNSOpaque16); // fudge + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + macsize = mDNSVal16(*(mDNSOpaque16 *)rd); + rd += sizeof(mDNSOpaque16); // MAC size + if (rd + macsize > rdend) goto finish; + rd += macsize; + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + rd += sizeof(mDNSOpaque16); // orig id + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + err = mDNSVal16(*(mDNSOpaque16 *)rd); // error code + + if (err == TSIG_ErrBadSig) { LogMsg("%##s: bad signature", displayname->c); err = mStatus_BadSig; } + else if (err == TSIG_ErrBadKey) { LogMsg("%##s: bad key", displayname->c); err = mStatus_BadKey; } + else if (err == TSIG_ErrBadTime) { LogMsg("%##s: bad time", displayname->c); err = mStatus_BadTime; } + else if (err) { LogMsg("%##s: unknown tsig error %d", displayname->c, err); err = mStatus_UnknownErr; } + goto finish; + } + } + + finish: + return err; + } + +mDNSlocal mStatus checkUpdateResult(domainname *displayname, mDNSu8 rcode, mDNS *m, const DNSMessage *msg, const mDNSu8 *end) + { + (void)msg; // currently unused, needed for TSIG errors + if (!rcode) return mStatus_NoError; + else if (rcode == kDNSFlag1_RC_YXDomain) + { + debugf("name in use: %##s", displayname->c); + return mStatus_NameConflict; + } + else if (rcode == kDNSFlag1_RC_Refused) + { + LogMsg("Update %##s refused", displayname->c); + return mStatus_Refused; + } + else if (rcode == kDNSFlag1_RC_NXRRSet) + { + LogMsg("Reregister refused (NXRRSET): %##s", displayname->c); + return mStatus_NoSuchRecord; + } + else if (rcode == kDNSFlag1_RC_NotAuth) + { + // TSIG errors should come with FmtErr as per RFC 2845, but BIND 9 sends them with NotAuth so we look here too + mStatus tsigerr = ParseTSIGError(m, msg, end, displayname); + if (!tsigerr) + { + LogMsg("Permission denied (NOAUTH): %##s", displayname->c); + return mStatus_UnknownErr; + } + else return tsigerr; + } + else if (rcode == kDNSFlag1_RC_FmtErr) + { + mStatus tsigerr = ParseTSIGError(m, msg, end, displayname); + if (!tsigerr) + { + LogMsg("Format Error: %##s", displayname->c); + return mStatus_UnknownErr; + } + else return tsigerr; + } + else + { + LogMsg("Update %##s failed with rcode %d", displayname->c, rcode); + return mStatus_UnknownErr; + } + } + +mDNSlocal void hndlServiceUpdateReply(mDNS * const m, ServiceRecordSet *srs, mStatus err) + { + mDNSBool InvokeCallback = mDNSfalse; + uDNS_RegInfo *info = &srs->uDNS_info; + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + ExtraResourceRecord **e = &srs->Extras; + AuthRecord *txt = &srs->RR_TXT; + uDNS_RegInfo *txtInfo = &txt->uDNS_info; + switch (info->state) + { + case regState_Pending: + if (err == mStatus_NameConflict && !info->TestForSelfConflict) + { + info->TestForSelfConflict = mDNStrue; + debugf("checking for self-conflict of service %##s", srs->RR_SRV.resrec.name->c); + SendServiceRegistration(m, srs); + return; + } + else if (info->TestForSelfConflict) + { + info->TestForSelfConflict = mDNSfalse; + if (err == mStatus_NoSuchRecord) err = mStatus_NameConflict; // NoSuchRecord implies that our prereq was not met, so we actually have a name conflict + if (err) info->state = regState_Unregistered; + else info->state = regState_Registered; + InvokeCallback = mDNStrue; + break; + } + else if (err == mStatus_UnknownErr && info->lease) + { + LogMsg("Re-trying update of service %##s without lease option", srs->RR_SRV.resrec.name->c); + info->lease = mDNSfalse; + SendServiceRegistration(m, srs); + return; + } + else + { + if (err) { LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name->c); info->state = regState_Unregistered; } //!!!KRS make sure all structs will still get cleaned up when client calls DeregisterService with this state + else info->state = regState_Registered; + InvokeCallback = mDNStrue; + break; + } + case regState_Refresh: + if (err) + { + LogMsg("Error %ld for refresh of service %##s", err, srs->RR_SRV.resrec.name->c); + InvokeCallback = mDNStrue; + info->state = regState_Unregistered; + } + else info->state = regState_Registered; + break; + case regState_DeregPending: + if (err) LogMsg("Error %ld for deregistration of service %##s", err, srs->RR_SRV.resrec.name->c); + if (info->SRVChanged) + { + info->state = regState_NoTarget; // NoTarget will allow us to pick up new target OR nat traversal state + break; + } + err = mStatus_MemFree; + InvokeCallback = mDNStrue; + if (nat) + { + if (nat->state == NATState_Deleted) { info->NATinfo = mDNSNULL; FreeNATInfo(m, nat); } // deletion copmleted + else nat->reg.ServiceRegistration = mDNSNULL; // allow mapping deletion to continue + } + info->state = regState_Unregistered; + break; + case regState_DeregDeferred: + if (err) + { + debugf("Error %ld received prior to deferred derigstration of %##s", err, srs->RR_SRV.resrec.name->c); + err = mStatus_MemFree; + InvokeCallback = mDNStrue; + info->state = regState_Unregistered; + break; + } + else + { + debugf("Performing deferred deregistration of %##s", srs->RR_SRV.resrec.name->c); + info->state = regState_Registered; + SendServiceDeregistration(m, srs); + return; + } + case regState_UpdatePending: + if (err) + { + LogMsg("hndlServiceUpdateReply: error updating TXT record for service %##s", srs->RR_SRV.resrec.name->c); + info->state = regState_Unregistered; + InvokeCallback = mDNStrue; + } + else + { + info->state = regState_Registered; + // deallocate old RData + if (txtInfo->UpdateRDCallback) txtInfo->UpdateRDCallback(m, txt, txtInfo->OrigRData); + SetNewRData(&txt->resrec, txtInfo->InFlightRData, txtInfo->InFlightRDLen); + txtInfo->OrigRData = mDNSNULL; + txtInfo->InFlightRData = mDNSNULL; + } + break; + case regState_FetchingZoneData: + case regState_Registered: + case regState_Cancelled: + case regState_Unregistered: + case regState_NATMap: + case regState_NoTarget: + case regState_ExtraQueued: + case regState_NATError: + LogMsg("hndlServiceUpdateReply called for service %##s in unexpected state %d with error %ld. Unlinking.", + srs->RR_SRV.resrec.name->c, info->state, err); + err = mStatus_UnknownErr; + } + + if ((info->SRVChanged || info->SRVUpdateDeferred) && (info->state == regState_NoTarget || info->state == regState_Registered)) + { + if (InvokeCallback) + { + info->ClientCallbackDeferred = mDNStrue; + info->DeferredStatus = err; + } + info->SRVChanged = mDNSfalse; + UpdateSRV(m, srs); + return; + } + + while (*e) + { + uDNS_RegInfo *einfo = &(*e)->r.uDNS_info; + if (einfo->state == regState_ExtraQueued) + { + if (info->state == regState_Registered && !err) + { + // extra resource record queued for this service - copy zone info and register + AssignDomainName(&einfo->zone, &info->zone); + einfo->ns = info->ns; + einfo->port = info->port; + einfo->lease = info->lease; + sendRecordRegistration(m, &(*e)->r); + e = &(*e)->next; + } + else if (err && einfo->state != regState_Unregistered) + { + // unlink extra from list + einfo->state = regState_Unregistered; + *e = (*e)->next; + } + else e = &(*e)->next; + } + else e = &(*e)->next; + } + + srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc. + if (info->state == regState_Unregistered) unlinkSRS(m, srs); + else if (txtInfo->QueuedRData && info->state == regState_Registered) + { + if (InvokeCallback) + { + // if we were supposed to give a client callback, we'll do it after we update the primary txt record + info->ClientCallbackDeferred = mDNStrue; + info->DeferredStatus = err; + } + info->state = regState_UpdatePending; + txtInfo->InFlightRData = txtInfo->QueuedRData; + txtInfo->InFlightRDLen = txtInfo->QueuedRDLen; + info->OrigRData = txt->resrec.rdata; + info->OrigRDLen = txt->resrec.rdlength; + txtInfo->QueuedRData = mDNSNULL; + SendServiceRegistration(m, srs); + return; + } + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (InvokeCallback) srs->ServiceCallback(m, srs, err); + else if (info->ClientCallbackDeferred) + { + info->ClientCallbackDeferred = mDNSfalse; + srs->ServiceCallback(m, srs, info->DeferredStatus); + } + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + // NOTE: do not touch structures after calling ServiceCallback + } + +mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err) + { + uDNS_RegInfo *info = &rr->uDNS_info; + mDNSBool InvokeCallback = mDNStrue; + + if (info->state == regState_UpdatePending) + { + if (err) + { + LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err); + info->state = regState_Unregistered; + } + else + { + debugf("Update record %##s - success", rr->resrec.name->c); + info->state = regState_Registered; + // deallocate old RData + if (info->UpdateRDCallback) info->UpdateRDCallback(m, rr, info->OrigRData); + SetNewRData(&rr->resrec, info->InFlightRData, info->InFlightRDLen); + info->OrigRData = mDNSNULL; + info->InFlightRData = mDNSNULL; + } + } + + if (info->state == regState_DeregPending) + { + debugf("Received reply for deregister record %##s type %d", rr->resrec.name->c, rr->resrec.rrtype); + if (err) LogMsg("ERROR: Deregistration of record %##s type %d failed with error %ld", + rr->resrec.name->c, rr->resrec.rrtype, err); + err = mStatus_MemFree; + info->state = regState_Unregistered; + } + + if (info->state == regState_DeregDeferred) + { + if (err) + { + LogMsg("Cancelling deferred deregistration record %##s type %d due to registration error %ld", + rr->resrec.name->c, rr->resrec.rrtype, err); + info->state = regState_Unregistered; + } + debugf("Calling deferred deregistration of record %##s type %d", rr->resrec.name->c, rr->resrec.rrtype); + info->state = regState_Registered; + uDNS_DeregisterRecord(m, rr); + return; + } + + if (info->state == regState_Pending || info->state == regState_Refresh) + { + if (!err) + { + info->state = regState_Registered; + if (info->state == regState_Refresh) InvokeCallback = mDNSfalse; + } + else + { + if (info->lease && err == mStatus_UnknownErr) + { + LogMsg("Re-trying update of record %##s without lease option", rr->resrec.name->c); + info->lease = mDNSfalse; + sendRecordRegistration(m, rr); + return; + } + LogMsg("Registration of record %##s type %d failed with error %ld", rr->resrec.name->c, rr->resrec.rrtype, err); + info->state = regState_Unregistered; + } + } + + if (info->state == regState_Unregistered) unlinkAR(&m->uDNS_info.RecordRegistrations, rr); + else rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc. + + if (info->QueuedRData && info->state == regState_Registered) + { + info->state = regState_UpdatePending; + info->InFlightRData = info->QueuedRData; + info->InFlightRDLen = info->QueuedRDLen; + info->OrigRData = rr->resrec.rdata; + info->OrigRDLen = rr->resrec.rdlength; + info->QueuedRData = mDNSNULL; + sendRecordRegistration(m, rr); + return; + } + + if (InvokeCallback) + { + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } + } + + +mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, uDNS_RegInfo *info) + { + LargeCacheRecord lcr; + const mDNSu8 *ptr; + int i; + mDNSu32 lease = 0; + mDNSs32 expire; + + ptr = LocateAdditionals(msg, end); + + if (info->lease && (ptr = LocateAdditionals(msg, end))) + { + for (i = 0; i < msg->h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); + if (!ptr) break; + if (lcr.r.resrec.rrtype == kDNSType_OPT) + { + if (lcr.r.resrec.rdlength < LEASE_OPT_RDLEN) continue; + if (lcr.r.resrec.rdata->u.opt.opt != kDNSOpt_Lease) continue; + lease = lcr.r.resrec.rdata->u.opt.OptData.lease; + break; + } + } + } + + if (lease > 0) + { + expire = (mDNSPlatformTimeNow(m) + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4); + if (info->state == regState_UpdatePending) + // if updating individual record, the service record set may expire sooner + { if (expire - info->expire < 0) info->expire = expire; } + else info->expire = expire; + } + else info->lease = mDNSfalse; + } + +mDNSexport void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *ptr = u->NATTraversals; + NATOp_t op; + + // check length, version, opcode + if (len < sizeof(NATPortMapReply) && len < sizeof(NATAddrReply)) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; } + if (pkt[0] != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expect version %d)", pkt[0], NATMAP_VERS); return; } + op = pkt[1]; + if (!(op & NATMAP_RESPONSE_MASK)) { LogMsg("Received NAT Traversal message that is not a response (opcode %d)", op); return; } + + while (ptr) + { + if ((ptr->state == NATState_Request || ptr->state == NATState_Refresh) && (ptr->op | NATMAP_RESPONSE_MASK) == op) + if (ptr->ReceiveResponse(ptr, m, pkt, len)) break; // note callback may invalidate ptr if it return value is non-zero + ptr = ptr->next; + } + } + +mDNSlocal const domainname *DNSRelayTestQuestion = (domainname*) + "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest" + "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\x7" "in-addr" "\x4" "arpa"; + +// Returns mDNStrue if response was handled +mDNSlocal mDNSBool uDNS_ReceiveTestQuestionResponse(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSInterfaceID InterfaceID) + { + const mDNSu8 *ptr = msg->data; + DNSQuestion q; + DNSServer *s; + mDNSu32 result = 0; + mDNSBool found = mDNSfalse; + + // 1. Find out if this is an answer to one of our test questions + if (msg->h.numQuestions != 1) return(mDNSfalse); + ptr = getQuestion(msg, ptr, end, InterfaceID, &q); + if (!ptr) return(mDNSfalse); + if (q.qtype != kDNSType_PTR || q.qclass != kDNSClass_IN) return(mDNSfalse); + if (!SameDomainName(&q.qname, DNSRelayTestQuestion)) return(mDNSfalse); + + // 2. If the DNS relay gave us a positive response, then it's got buggy firmware + // else, if the DNS relay gave us an error or no-answer response, it passed our test + if ((msg->h.flags.b[1] & kDNSFlag1_RC) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0) + result = DNSServer_Failed; + else + result = DNSServer_Passed; + + // 3. Find occurrences of this server in our list, and mark them appropriately + for (s = m->uDNS_info.Servers; s; s = s->next) + if (mDNSSameAddress(srcaddr, &s->addr) && s->teststate != result) + { s->teststate = result; found = mDNStrue; } + + // 4. Assuming we found the server in question in our list (don't want to risk being victim of a deliberate DOS attack here) + // log a message to let the user know why Wide-Area Service Discovery isn't working + if (found && result == DNSServer_Failed) + LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a.", srcaddr); + + return(mDNStrue); // Return mDNStrue to tell uDNS_ReceiveMsg it doens't need to process this packet further + } + +mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, + const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) + { + DNSQuestion *qptr; + AuthRecord *rptr; + ServiceRecordSet *sptr; + mStatus err = mStatus_NoError; + uDNS_GlobalInfo *u = &m->uDNS_info; + + mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + mDNSu8 rcode = (mDNSu8)(msg->h.flags.b[1] & kDNSFlag1_RC); + + mDNSs32 timenow = mDNSPlatformTimeNow(m); + + // unused + (void)dstaddr; + (void)dstport; + (void)InterfaceID; + + if (QR_OP == StdR) + { + // !!!KRS we should to a table lookup here to see if it answers an LLQ or a 1-shot + // LLQ Responses over TCP not currently supported + if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport, InterfaceID)) return; + + if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, InterfaceID)) return; + + for (qptr = u->ActiveQueries; qptr; qptr = qptr->next) + { + //!!!KRS we should have a hashtable, hashed on message id + if (qptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) + { + if (timenow - (qptr->LastQTime + RESPONSE_WINDOW) > 0) + { debugf("uDNS_ReceiveMsg - response received after maximum allowed window. Discarding"); return; } + if (msg->h.flags.b[0] & kDNSFlag0_TC) + { hndlTruncatedAnswer(qptr, srcaddr, m); return; } + else + { + u->CurrentQuery = qptr; + qptr->uDNS_info.responseCallback(m, msg, end, qptr, qptr->uDNS_info.context); + u->CurrentQuery = mDNSNULL; + // Note: responseCallback can invalidate qptr + return; + } + } + } + } + if (QR_OP == UpdateR) + { + for (sptr = u->ServiceRegistrations; sptr; sptr = sptr->next) + { + if (sptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) + { + err = checkUpdateResult(sptr->RR_SRV.resrec.name, rcode, m, msg, end); + if (!err) SetUpdateExpiration(m, msg, end, &sptr->uDNS_info); + hndlServiceUpdateReply(m, sptr, err); + return; + } + } + for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next) + { + if (rptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) + { + err = checkUpdateResult(rptr->resrec.name, rcode, m, msg, end); + if (!err) SetUpdateExpiration(m, msg, end, &rptr->uDNS_info); + hndlRecordUpdateReply(m, rptr, err); + return; + } + } + } + debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id)); + } + +// lookup a DNS Server, matching by name in split-dns configurations. Result stored in addr parameter if successful +mDNSlocal DNSServer *GetServerForName(uDNS_GlobalInfo *u, const domainname *name) + { + DNSServer *curmatch = mDNSNULL, *p = u->Servers; + int i, curmatchlen = -1; + int ncount = name ? CountLabels(name) : 0; + + while (p) + { + int scount = CountLabels(&p->domain); + if (scount <= ncount && scount > curmatchlen) + { + // only inspect if server's domain is longer than current best match and shorter than the name itself + const domainname *tail = name; + for (i = 0; i < ncount - scount; i++) + tail = (domainname *)(tail->c + 1 + tail->c[0]); // find "tail" (scount labels) of name + if (SameDomainName(tail, &p->domain)) { curmatch = p; curmatchlen = scount; } + } + p = p->next; + } + return(curmatch); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Query Routines +#endif + +#define sameID(x,y) mDNSPlatformMemSame(x,y,8) + +mDNSlocal void initializeQuery(DNSMessage *msg, DNSQuestion *question) + { + ubzero(msg, sizeof(msg)); + InitializeDNSMessage(&msg->h, question->uDNS_info.id, uQueryFlags); + } + +mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question) + { + initializeQuery(msg, question); + + *endPtr = putQuestion(msg, msg->data, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); + if (!*endPtr) + { + LogMsg("ERROR: Unicast query out of space in packet"); + return mStatus_UnknownErr; + } + return mStatus_NoError; + } + +mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *question, LLQOptData *data, mDNSBool includeQuestion) + { + AuthRecord rr; + ResourceRecord *opt = &rr.resrec; + rdataOpt *optRD; + + //!!!KRS when we implement multiple llqs per message, we'll need to memmove anything past the question section + if (includeQuestion) + { + ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); + if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return mDNSNULL; } + } + // locate OptRR if it exists, set pointer to end + // !!!KRS implement me + + + // format opt rr (fields not specified are zero-valued) + ubzero(&rr, sizeof(AuthRecord)); + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + opt->rdlength = LLQ_OPT_RDLEN; + opt->rdestimate = LLQ_OPT_RDLEN; + + optRD = &rr.resrec.rdata->u.opt; + optRD->opt = kDNSOpt_LLQ; + optRD->optlen = LLQ_OPTLEN; + umemcpy(&optRD->OptData.llq, data, sizeof(*data)); + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0); + if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; } + + return ptr; + } + + +mDNSlocal mDNSBool getLLQAtIndex(mDNS *m, DNSMessage *msg, const mDNSu8 *end, LLQOptData *llq, int index) + { + LargeCacheRecord lcr; + int i; + const mDNSu8 *ptr; + + ubzero(&lcr, sizeof(lcr)); + + ptr = LocateAdditionals(msg, end); + if (!ptr) return mDNSfalse; + + // find the last additional + for (i = 0; i < msg->h.numAdditionals; i++) +// { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; } +//!!!KRS workaround for LH server bug, which puts OPT as first additional + { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; if (lcr.r.resrec.rrtype == kDNSType_OPT) break; } + if (lcr.r.resrec.rrtype != kDNSType_OPT) return mDNSfalse; + if (lcr.r.resrec.rdlength < (index + 1) * LLQ_OPT_RDLEN) return mDNSfalse; // rdata too small + umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(*llq)), sizeof(*llq)); + return mDNStrue; + } + +mDNSlocal void recvRefreshReply(mDNS *m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *q) + { + LLQ_Info *qInfo; + LLQOptData pktData; + + qInfo = q->uDNS_info.llq; + if (!getLLQAtIndex(m, msg, end, &pktData, 0)) { LogMsg("ERROR recvRefreshReply - getLLQAtIndex"); return; } + if (pktData.llqOp != kLLQOp_Refresh) return; + if (!sameID(pktData.id, qInfo->id)) { LogMsg("recvRefreshReply - ID mismatch. Discarding"); return; } + if (pktData.err != LLQErr_NoError) { LogMsg("recvRefreshReply: received error %d from server", pktData.err); return; } + + qInfo->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond); + qInfo->retry = qInfo->expire - ((mDNSs32)pktData.lease * mDNSPlatformOneSecond/2); + + qInfo->origLease = pktData.lease; + qInfo->state = LLQ_Established; + } + +mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease) + { + DNSMessage msg; + mDNSu8 *end; + LLQOptData llq; + LLQ_Info *info = q->uDNS_info.llq; + mStatus err; + mDNSs32 timenow; + + timenow = mDNSPlatformTimeNow(m); + if ((info->state == LLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES) || + info->expire - timenow < 0) + { + LogMsg("Unable to refresh LLQ %##s - will retry in %d minutes", q->qname.c, kLLQ_DEF_RETRY/60); + info->state = LLQ_Retry; + info->retry = mDNSPlatformTimeNow(m) + kLLQ_DEF_RETRY * mDNSPlatformOneSecond; + info->deriveRemovesOnResume = mDNStrue; + return; + //!!!KRS handle this - periodically try to re-establish + } + + llq.vers = kLLQ_Vers; + llq.llqOp = kLLQOp_Refresh; + llq.err = LLQErr_NoError; + umemcpy(llq.id, info->id, 8); + llq.lease = lease; + + initializeQuery(&msg, q); + end = putLLQ(&msg, msg.data, q, &llq, mDNStrue); + if (!end) { LogMsg("ERROR: sendLLQRefresh - putLLQ"); return; } + + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) debugf("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %ld", err); + + if (info->state == LLQ_Established) info->ntries = 1; + else info->ntries++; + info->state = LLQ_Refresh; + q->LastQTime = timenow; + info->retry = (info->expire - q->LastQTime) / 2; + } + +mDNSlocal mDNSBool recvLLQEvent(mDNS *m, DNSQuestion *q, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, mDNSInterfaceID InterfaceID) + { + DNSMessage ack; + mDNSu8 *ackEnd = ack.data; + mStatus err; + LLQOptData opt; + + (void)InterfaceID; // unused + + // find Opt RR, verify correct ID + if (!getLLQAtIndex(m, msg, end, &opt, 0)) { debugf("Pkt does not contain LLQ Opt"); return mDNSfalse; } + if (!q->uDNS_info.llq) { LogMsg("Error: recvLLQEvent - question object does not contain LLQ metadata"); return mDNSfalse; } + if (!sameID(opt.id, q->uDNS_info.llq->id)) { return mDNSfalse; } + if (opt.llqOp != kLLQOp_Event) { if (!q->uDNS_info.llq->ntries) LogMsg("recvLLQEvent - Bad LLQ Opcode %d", opt.llqOp); return mDNSfalse; } + + // invoke response handler + m->uDNS_info.CurrentQuery = q; + q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); + if (m->uDNS_info.CurrentQuery != q) return mDNStrue; + + // format and send ack + InitializeDNSMessage(&ack.h, msg->h.id, ResponseFlags); + ackEnd = putLLQ(&ack, ack.data, mDNSNULL, &opt, mDNSfalse); + if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putLLQ"); return mDNSfalse; } + err = mDNSSendDNSMessage(m, &ack, ackEnd, mDNSInterface_Any, srcaddr, srcport, -1, mDNSNULL); + if (err) debugf("ERROR: recvLLQEvent - mDNSSendDNSMessage returned %ld", err); + return mDNStrue; + } + + + +mDNSlocal void hndlChallengeResponseAck(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q) + { + LLQ_Info *info = q->uDNS_info.llq; + + if (llq->err) { LogMsg("hndlChallengeResponseAck - received error %d from server", llq->err); goto error; } + if (!sameID(info->id, llq->id)) { LogMsg("hndlChallengeResponseAck - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering) + info->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); + info->retry = info->expire - ((mDNSs32)llq->lease * mDNSPlatformOneSecond / 2); + + info->origLease = llq->lease; + info->state = LLQ_Established; + + q->uDNS_info.responseCallback = llqResponseHndlr; + llqResponseHndlr(m, pktMsg, end, q, mDNSNULL); + return; + + error: + info->state = LLQ_Error; + } + +mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) + { + LLQ_Info *info = q->uDNS_info.llq; + DNSMessage response; + mDNSu8 *responsePtr = response.data; + mStatus err; + LLQOptData llqBuf; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + + if (info->ntries++ == kLLQ_MAX_TRIES) + { + LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s. Will re-try in %d minutes", + kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60); + info->state = LLQ_Retry; + info->retry = timenow + (kLLQ_DEF_RETRY * mDNSPlatformOneSecond); + // !!!KRS give a callback error in these cases? + return; + } + + if (!llq) + { + llq = &llqBuf; + llq->vers = kLLQ_Vers; + llq->llqOp = kLLQOp_Setup; + llq->err = LLQErr_NoError; + umemcpy(llq->id, info->id, 8); + llq->lease = info->origLease; + } + + q->LastQTime = timenow; + info->retry = timenow + (kLLQ_INIT_RESEND * info->ntries * mDNSPlatformOneSecond); + + if (constructQueryMsg(&response, &responsePtr, q)) goto error; + responsePtr = putLLQ(&response, responsePtr, q, llq, mDNSfalse); + if (!responsePtr) { LogMsg("ERROR: sendChallengeResponse - putLLQ"); goto error; } + + err = mDNSSendDNSMessage(m, &response, responsePtr, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) debugf("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %ld", err); + // on error, we procede as normal and retry after the appropriate interval + + return; + + error: + info->state = LLQ_Error; + } + + + +mDNSlocal void hndlRequestChallenge(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q) + { + LLQ_Info *info = q->uDNS_info.llq; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + switch(llq->err) + { + case LLQErr_NoError: break; + case LLQErr_ServFull: + LogMsg("hndlRequestChallenge - received ServFull from server for LLQ %##s. Retry in %lu sec", q->qname.c, llq->lease); + info->retry = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); + info->state = LLQ_Retry; + simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL); // get available answers + info->deriveRemovesOnResume = mDNStrue; + case LLQErr_Static: + info->state = LLQ_Static; + LogMsg("LLQ %##s: static", q->qname.c); + simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL); + return; + case LLQErr_FormErr: + LogMsg("ERROR: hndlRequestChallenge - received FormErr from server for LLQ %##s", q->qname.c); + goto error; + case LLQErr_BadVers: + LogMsg("ERROR: hndlRequestChallenge - received BadVers from server"); + goto error; + case LLQErr_UnknownErr: + LogMsg("ERROR: hndlRequestChallenge - received UnknownErr from server for LLQ %##s", q->qname.c); + goto error; + default: + LogMsg("ERROR: hndlRequestChallenge - received invalid error %d for LLQ %##s", llq->err, q->qname.c); + goto error; + } + + if (info->origLease != llq->lease) + debugf("hndlRequestChallenge: requested lease %lu, granted lease %lu", info->origLease, llq->lease); + + // cache expiration in case we go to sleep before finishing setup + info->origLease = llq->lease; + info->expire = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); + + // update state + info->state = LLQ_SecondaryRequest; + umemcpy(info->id, llq->id, 8); + info->ntries = 0; // first attempt to send response + + sendChallengeResponse(m, q, llq); + return; + + + error: + info->state = LLQ_Error; + } + + +// response handler for initial and secondary setup responses +mDNSlocal void recvSetupResponse(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, DNSQuestion *q, void *clientContext) + { + DNSQuestion pktQuestion; + LLQOptData llq; + const mDNSu8 *ptr = pktMsg->data; + LLQ_Info *info = q->uDNS_info.llq; + mDNSu8 rcode = (mDNSu8)(pktMsg->h.flags.b[1] & kDNSFlag1_RC); + + (void)clientContext; // unused + + if (rcode && rcode != kDNSFlag1_RC_NXDomain) goto poll; + + ptr = getQuestion(pktMsg, ptr, end, 0, &pktQuestion); + if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto poll; } + if (!SameDomainName(&q->qname, &pktQuestion.qname)) + { LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %##s", q->qname.c); goto poll; } + + if (!getLLQAtIndex(m, pktMsg, end, &llq, 0)) { debugf("recvSetupResponse - GetLLQAtIndex"); goto poll; } + if (llq.llqOp != kLLQOp_Setup) { LogMsg("ERROR: recvSetupResponse - bad op %d", llq.llqOp); goto poll; } + if (llq.vers != kLLQ_Vers) { LogMsg("ERROR: recvSetupResponse - bad vers %d", llq.vers); goto poll; } + + if (info->state == LLQ_InitialRequest) { hndlRequestChallenge(m, pktMsg, end, &llq, q); return; } + if (info->state == LLQ_SecondaryRequest) { hndlChallengeResponseAck(m, pktMsg, end, &llq, q); return; } + LogMsg("recvSetupResponse - bad state %d", info->state); + + poll: + info->state = LLQ_Poll; + q->uDNS_info.responseCallback = llqResponseHndlr; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + } + +mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer) + { + DNSMessage msg; + mDNSu8 *end; + LLQOptData llqData; + DNSQuestion *q = info->question; + mStatus err = mStatus_NoError; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + uDNS_GlobalInfo *u = &m->uDNS_info; + + if (IsPrivateV4Addr(&u->AdvertisedV4)) + { + if (!u->LLQNatInfo) + { + info->state = LLQ_NatMapWait; + StartLLQNatMap(m); + return; + } + if (u->LLQNatInfo->state == NATState_Error) goto poll; + if (u->LLQNatInfo->state != NATState_Established && u->LLQNatInfo->state != NATState_Legacy) + { info->state = LLQ_NatMapWait; info->NATMap = mDNStrue; return; } + info->NATMap = mDNStrue; // this llq references the global llq nat mapping + } + + if (info->ntries++ >= kLLQ_MAX_TRIES) + { + debugf("startLLQHandshake: %d failed attempts for LLQ %##s. Polling.", kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60); + goto poll; + } + + // set llq rdata + llqData.vers = kLLQ_Vers; + llqData.llqOp = kLLQOp_Setup; + llqData.err = LLQErr_NoError; + ubzero(llqData.id, 8); + llqData.lease = kLLQ_DefLease; + + initializeQuery(&msg, q); + end = putLLQ(&msg, msg.data, q, &llqData, mDNStrue); + if (!end) + { + LogMsg("ERROR: startLLQHandshake - putLLQ"); + info->state = LLQ_Error; + return; + } + + if (!defer) // if we are to defer, we simply set the retry timers so the request goes out in the future + { + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) debugf("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %ld", err); + // on error, we procede as normal and retry after the appropriate interval + } + + // update question/info state + info->state = LLQ_InitialRequest; + info->origLease = kLLQ_DefLease; + info->retry = timenow + (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); + q->LastQTime = timenow; + q->uDNS_info.responseCallback = recvSetupResponse; + q->uDNS_info.internal = mDNStrue; + return; + + poll: + info->question->uDNS_info.responseCallback = llqResponseHndlr; + info->state = LLQ_Poll; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + } + +// wrapper for startLLQHandshake, invoked by async op callback +mDNSlocal void startLLQHandshakeCallback(mStatus err, mDNS *const m, void *llqInfo, const AsyncOpResult *result) + { + LLQ_Info *info = (LLQ_Info *)llqInfo; + const zoneData_t *zoneInfo = mDNSNULL; + + // check state first to make sure it is OK to touch question object + if (info->state == LLQ_Cancelled) + { + // StopQuery was called while we were getting the zone info + debugf("startLLQHandshake - LLQ Cancelled."); + info->question = mDNSNULL; // question may be deallocated + ufree(info); + return; + } + + if (!info->question) + { LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL question"); goto error; } + + if (info->state != LLQ_GetZoneInfo) + { LogMsg("ERROR: startLLQHandshake - bad state %d", info->state); goto error; } + + if (err) + { LogMsg("ERROR: startLLQHandshakeCallback %##s invoked with error code %ld", info->question->qname.c, err); goto poll; } + + if (!result) + { LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL result and no error code"); goto error; } + + zoneInfo = &result->zoneData; + + if (!zoneInfo->llqPort.NotAnInteger) + { debugf("LLQ port lookup failed - reverting to polling"); info->servPort.NotAnInteger = 0; goto poll; } + + // cache necessary zone data + info->servAddr = zoneInfo->primaryAddr; + info->servPort = zoneInfo->llqPort; + info->ntries = 0; + + if (info->state == LLQ_SuspendDeferred) info->state = LLQ_Suspended; + else startLLQHandshake(m, info, mDNSfalse); + return; + + poll: + info->question->uDNS_info.responseCallback = llqResponseHndlr; + info->state = LLQ_Poll; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + return; + + error: + info->state = LLQ_Error; + } + +mDNSlocal mStatus startLLQ(mDNS *m, DNSQuestion *question) + { + LLQ_Info *info; + mStatus err = mStatus_NoError; + + // allocate / init info struct + info = umalloc(sizeof(LLQ_Info)); + if (!info) { LogMsg("ERROR: startLLQ - malloc"); return mStatus_NoMemoryErr; } + ubzero(info, sizeof(LLQ_Info)); + info->state = LLQ_GetZoneInfo; + + // link info/question + info->question = question; + question->uDNS_info.llq = info; + + question->uDNS_info.responseCallback = llqResponseHndlr; + + err = startGetZoneData(&question->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, info); + if (err) + { + LogMsg("ERROR: startLLQ - startGetZoneData returned %ld", err); + info->question = mDNSNULL; + ufree(info); + question->uDNS_info.llq = mDNSNULL; + return err; + } + + LinkActiveQuestion(&m->uDNS_info, question); + return err; + } + +mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID) + { + DNSQuestion pktQ, *q; + uDNS_GlobalInfo *u = &m->uDNS_info; + const mDNSu8 *ptr = msg->data; + LLQ_Info *llqInfo; + + if (!msg->h.numQuestions) return mDNSfalse; + + ptr = getQuestion(msg, ptr, end, 0, &pktQ); + if (!ptr) return mDNSfalse; + pktQ.uDNS_info.id = msg->h.id; + + q = u->ActiveQueries; + while (q) + { + llqInfo = q->uDNS_info.llq; + if (q->LongLived && + llqInfo && + q->qnamehash == pktQ.qnamehash && + q->qtype == pktQ.qtype && + SameDomainName(&q->qname, &pktQ.qname)) + { + u->CurrentQuery = q; + if (llqInfo->state == LLQ_Established || (llqInfo->state == LLQ_Refresh && msg->h.numAnswers)) + { if (recvLLQEvent(m, q, msg, end, srcaddr, srcport, InterfaceID)) return mDNStrue; } + else if (msg->h.id.NotAnInteger == q->uDNS_info.id.NotAnInteger) + { + if (llqInfo->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers) + { recvRefreshReply(m, msg, end, q); return mDNStrue; } + if (llqInfo->state < LLQ_Static) + { + if ((llqInfo->state != LLQ_InitialRequest && llqInfo->state != LLQ_SecondaryRequest) || mDNSSameAddress(srcaddr, &llqInfo->servAddr)) + { q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); return mDNStrue; } + } + } + } + q = q->next; + } + return mDNSfalse; + } + +mDNSexport mDNSBool uDNS_IsActiveQuery(DNSQuestion *const question, uDNS_GlobalInfo *u) + { + DNSQuestion *q; + + for (q = u->ActiveQueries; q; q = q->next) + { + if (q == question) + { + if (!question->uDNS_info.id.NotAnInteger || question->InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&question->qname)) + LogMsg("Warning: Question %##s in Active Unicast Query list with id %d, interfaceID %p", + question->qname.c, question->uDNS_info.id.NotAnInteger, question->InterfaceID); + return mDNStrue; + } + } + return mDNSfalse; + } + +// stopLLQ happens IN ADDITION to stopQuery +mDNSlocal void stopLLQ(mDNS *m, DNSQuestion *question) + { + LLQ_Info *info = question->uDNS_info.llq; + (void)m; // unused + + if (!question->LongLived) { LogMsg("ERROR: stopLLQ - LongLived flag not set"); return; } + if (!info) { LogMsg("ERROR: stopLLQ - llq info is NULL"); return; } + + switch (info->state) + { + case LLQ_UnInit: + LogMsg("ERROR: stopLLQ - state LLQ_UnInit"); + //!!!KRS should we unlink info<->question here? + return; + case LLQ_GetZoneInfo: + case LLQ_SuspendDeferred: + info->question = mDNSNULL; // remove ref to question, as it may be freed when we get called back from async op + info->state = LLQ_Cancelled; + return; + case LLQ_Established: + case LLQ_Refresh: + // refresh w/ lease 0 + sendLLQRefresh(m, question, 0); + goto end; + default: + debugf("stopLLQ - silently discarding LLQ in state %d", info->state); + goto end; + } + + end: + if (info->NATMap) info->NATMap = mDNSfalse; + CheckForUnreferencedLLQMapping(m); + info->question = mDNSNULL; + ufree(info); + question->uDNS_info.llq = mDNSNULL; + question->LongLived = mDNSfalse; + } + +mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSQuestion *qptr, *prev = mDNSNULL; + CacheRecord *ka; + + qptr = u->ActiveQueries; + while (qptr) + { + if (qptr == question) + { + if (question->LongLived && question->uDNS_info.llq) + stopLLQ(m, question); + if (m->uDNS_info.CurrentQuery == question) + m->uDNS_info.CurrentQuery = m->uDNS_info.CurrentQuery->next; + while (question->uDNS_info.knownAnswers) + { + ka = question->uDNS_info.knownAnswers; + question->uDNS_info.knownAnswers = question->uDNS_info.knownAnswers->next; + ufree(ka); + } + if (prev) prev->next = question->next; + else u->ActiveQueries = question->next; + return mStatus_NoError; + } + prev = qptr; + qptr = qptr->next; + } + LogMsg("uDNS_StopQuery: no such active query (%##s)", question->qname.c); + return mStatus_UnknownErr; + } + +mDNSlocal mStatus startQuery(mDNS *const m, DNSQuestion *const question, mDNSBool internal) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + //!!!KRS we should check if the question is already in our activequestion list + if (!ValidateDomainName(&question->qname)) + { + LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return mStatus_Invalid; + } + + question->next = mDNSNULL; + question->qnamehash = DomainNameHashValue(&question->qname); // to do quick domain name comparisons + question->uDNS_info.id = newMessageID(u); + question->uDNS_info.Answered = mDNSfalse; + + // break here if its and LLQ + if (question->LongLived) return startLLQ(m, question); + + question->ThisQInterval = INIT_UCAST_POLL_INTERVAL / 2; + question->LastQTime = mDNSPlatformTimeNow(m) - question->ThisQInterval; + // store the question/id in active question list + question->uDNS_info.internal = internal; + LinkActiveQuestion(u, question); + question->uDNS_info.knownAnswers = mDNSNULL; + LogOperation("uDNS startQuery: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + + return mStatus_NoError; + } + +mDNSexport mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question) + { + ubzero(&question->uDNS_info, sizeof(uDNS_QuestionInfo)); + question->uDNS_info.responseCallback = simpleResponseHndlr; + question->uDNS_info.context = mDNSNULL; + //LogOperation("uDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return startQuery(m, question, 0); + } + +// explicitly set response handler +mDNSlocal mStatus startInternalQuery(DNSQuestion *q, mDNS *m, InternalResponseHndlr callback, void *hndlrContext) + { + ubzero(&q->uDNS_info, sizeof(uDNS_QuestionInfo)); + q->QuestionContext = hndlrContext; + q->uDNS_info.responseCallback = callback; + q->uDNS_info.context = hndlrContext; + return startQuery(m, q, 1); + } + + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Domain -> Name Server Conversion +#endif + + +/* startGetZoneData + * + * Asynchronously find the address of the nameserver for the enclosing zone for a given domain name, + * i.e. the server to which update and LLQ requests will be sent for a given name. Once the address is + * derived, it will be passed to the callback, along with a context pointer. If the zone cannot + * be determined or if an error occurs, an all-zeros address will be passed and a message will be + * written to the syslog. + * + * If the FindUpdatePort arg is set, the port on which the server accepts dynamic updates is determined + * by querying for the _dns-update._udp.<zone>. SRV record. Likewise, if the FindLLQPort arg is set, + * the port on which the server accepts long lived queries is determined by querying for + * _dns-llq._udp.<zone>. record. If either of these queries fail, or flags are not specified, + * the llqPort and updatePort fields in the result structure are set to zero. + * + * Steps for deriving the zone name are as follows: + * + * Query for an SOA record for the required domain. If we don't get an answer (or an SOA in the Authority + * section), we strip the leading label from the name and repeat, until we get an answer. + * + * The name of the SOA record is our enclosing zone. The mname field in the SOA rdata is the domain + * name of the primary NS. + * + * We verify that there is an NS record with this zone for a name and the mname for its rdata. + * (!!!KRS this seems redundant, but BIND does this, and it should normally be zero-overhead since + * the NS query will get us address records in the additionals section, which we'd otherwise have to + * explicitly query for.) + * + * We then query for the address record for this nameserver (if it is not in the addionals section of + * the NS record response.) + */ + + +// state machine types and structs +// + +// state machine states +typedef enum + { + init, + lookupSOA, + foundZone, + lookupNS, + foundNS, + lookupA, + foundA, + lookupPort, + foundPort, + complete + } ntaState; + +// state machine actions +typedef enum + { + smContinue, // continue immediately to next state + smBreak, // break until next packet/timeout + smError // terminal error - cleanup and abort + } smAction; + +typedef struct + { + domainname origName; // name we originally try to convert + domainname *curSOA; // name we have an outstanding SOA query for + ntaState state; // determines what we do upon receiving a packet + mDNS *m; + domainname zone; // left-hand-side of SOA record + mDNSu16 zoneClass; + domainname ns; // mname in SOA rdata, verified in confirmNS state + mDNSv4Addr addr; // address of nameserver + DNSQuestion question; // storage for any active question + DNSQuestion extraQuestion; // additional storage + mDNSBool questionActive; // if true, StopQuery() can be called on the question field + mDNSBool findUpdatePort; + mDNSBool findLLQPort; + mDNSIPPort updatePort; + mDNSIPPort llqPort; + AsyncOpCallback *callback; // caller specified function to be called upon completion + void *callbackInfo; + } ntaContext; + + +// function prototypes (for routines that must be used as fn pointers prior to their definitions, +// and allows states to be read top-to-bottom in logical order) +mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr); +mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext *context); +mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr); +mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context); +mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context); +mDNSlocal smAction hndlLookupPorts(DNSMessage *msg, const mDNSu8 *end, ntaContext *context); + +// initialization +mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort, + AsyncOpCallback callback, void *callbackInfo) + { + ntaContext *context = (ntaContext*)umalloc(sizeof(ntaContext)); + if (!context) { LogMsg("ERROR: startGetZoneData - umalloc failed"); return mStatus_NoMemoryErr; } + ubzero(context, sizeof(ntaContext)); + AssignDomainName(&context->origName, name); + context->state = init; + context->m = m; + context->callback = callback; + context->callbackInfo = callbackInfo; + context->findUpdatePort = findUpdatePort; + context->findLLQPort = findLLQPort; + getZoneData(m, mDNSNULL, mDNSNULL, mDNSNULL, context); + return mStatus_NoError; + } + +// state machine entry routine +mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr) + { + AsyncOpResult result; + ntaContext *context = (ntaContext*)contextPtr; + smAction action; + + // unused + (void)m; + (void)question; + + // stop any active question + if (context->questionActive) + { + uDNS_StopQuery(context->m, &context->question); + context->questionActive = mDNSfalse; + } + + if (msg && msg->h.flags.b[2] >> 4 && msg->h.flags.b[2] >> 4 != kDNSFlag1_RC_NXDomain) + { + // rcode non-zero, non-nxdomain + LogMsg("ERROR: getZoneData - received response w/ rcode %d", msg->h.flags.b[2] >> 4); + goto error; + } + + switch (context->state) + { + case init: + case lookupSOA: + action = hndlLookupSOA(msg, end, context); + if (action == smError) goto error; + if (action == smBreak) return; + case foundZone: + case lookupNS: + action = confirmNS(msg, end, context); + if (action == smError) goto error; + if (action == smBreak) return; + case foundNS: + case lookupA: + action = lookupNSAddr(msg, end, context); + if (action == smError) goto error; + if (action == smBreak) return; + case foundA: + if (!context->findUpdatePort && !context->findLLQPort) + { + context->state = complete; + break; + } + case lookupPort: + action = hndlLookupPorts(msg, end, context); + if (action == smError) goto error; + if (action == smBreak) return; + if (action == smContinue) context->state = complete; + case foundPort: + case complete: break; + } + + if (context->state != complete) + { + LogMsg("ERROR: getZoneData - exited state machine with state %d", context->state); + goto error; + } + + result.type = zoneDataResult; + result.zoneData.primaryAddr.ip.v4 = context->addr; + result.zoneData.primaryAddr.type = mDNSAddrType_IPv4; + AssignDomainName(&result.zoneData.zoneName, &context->zone); + result.zoneData.zoneClass = context->zoneClass; + + if (context->findLLQPort) + result.zoneData.llqPort = context->llqPort; + else + result.zoneData.llqPort = zeroIPPort; + + if (context->findUpdatePort) + result.zoneData.updatePort = context->updatePort; + else + result.zoneData.updatePort = zeroIPPort; + + context->callback(mStatus_NoError, context->m, context->callbackInfo, &result); + goto cleanup; + +error: + if (context && context->callback) + context->callback(mStatus_UnknownErr, context->m, context->callbackInfo, mDNSNULL); +cleanup: + if (context && context->questionActive) + { + uDNS_StopQuery(context->m, &context->question); + context->questionActive = mDNSfalse; + } + if (context) ufree(context); + } + +mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext *context) + { + mStatus err; + LargeCacheRecord lcr; + ResourceRecord *rr = &lcr.r.resrec; + DNSQuestion *query = &context->question; + const mDNSu8 *ptr; + + if (msg) + { + // if msg contains SOA record in answer or authority sections, update context/state and return + int i; + ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { LogMsg("ERROR: hndlLookupSOA, Answers - GetLargeResourceRecord returned NULL"); return smError; } + if (rr->rrtype == kDNSType_SOA && SameDomainName(context->curSOA, rr->name)) + { + processSOA(context, rr); + return smContinue; + } + } + ptr = LocateAuthorities(msg, end); + // SOA not in answers, check in authority + for (i = 0; i < msg->h.numAuthorities; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); ///!!!KRS using type PacketAns for auth + if (!ptr) { LogMsg("ERROR: hndlLookupSOA, Authority - GetLargeResourceRecord returned NULL"); return smError; } + if (rr->rrtype == kDNSType_SOA) + { + processSOA(context, rr); + return smContinue; + } + } + } + + if (context->state != init && !context->curSOA->c[0]) + { + // we've gone down to the root and have not found an SOA + LogMsg("ERROR: hndlLookupSOA - recursed to root label of %##s without finding SOA", + context->origName.c); + return smError; + } + + ubzero(query, sizeof(DNSQuestion)); + // chop off leading label unless this is our first try + if (context->state == init) context->curSOA = &context->origName; + else context->curSOA = (domainname *)(context->curSOA->c + context->curSOA->c[0]+1); + + context->state = lookupSOA; + AssignDomainName(&query->qname, context->curSOA); + query->qtype = kDNSType_SOA; + query->qclass = kDNSClass_IN; + err = startInternalQuery(query, context->m, getZoneData, context); + context->questionActive = mDNStrue; + if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err); + + return smBreak; // break from state machine until we receive another packet + } + +mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr) + { + AssignDomainName(&context->zone, rr->name); + context->zoneClass = rr->rrclass; + AssignDomainName(&context->ns, &rr->rdata->u.soa.mname); + context->state = foundZone; + } + + +mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context) + { + DNSQuestion *query = &context->question; + mStatus err; + LargeCacheRecord lcr; + const ResourceRecord *const rr = &lcr.r.resrec; + const mDNSu8 *ptr; + int i; + + if (context->state == foundZone) + { + // we've just learned the zone. confirm that an NS record exists + AssignDomainName(&query->qname, &context->zone); + query->qtype = kDNSType_NS; + query->qclass = kDNSClass_IN; + err = startInternalQuery(query, context->m, getZoneData, context); + context->questionActive = mDNStrue; + if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission", err); + context->state = lookupNS; + return smBreak; // break from SM until we receive another packet + } + else if (context->state == lookupNS) + { + ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { LogMsg("ERROR: confirmNS, Answers - GetLargeResourceRecord returned NULL"); return smError; } + if (rr->rrtype == kDNSType_NS && + SameDomainName(&context->zone, rr->name) && SameDomainName(&context->ns, &rr->rdata->u.name)) + { + context->state = foundNS; + return smContinue; // next routine will examine additionals section of A record + } + } + debugf("ERROR: could not confirm existence of record %##s NS %##s", context->zone.c, context->ns.c); + return smError; + } + else { LogMsg("ERROR: confirmNS - bad state %d", context->state); return smError; } + } + +mDNSlocal smAction queryNSAddr(ntaContext *context) + { + mStatus err; + DNSQuestion *query = &context->question; + + AssignDomainName(&query->qname, &context->ns); + query->qtype = kDNSType_A; + query->qclass = kDNSClass_IN; + err = startInternalQuery(query, context->m, getZoneData, context); + context->questionActive = mDNStrue; + if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err); + context->state = lookupA; + return smBreak; + } + +mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context) + { + const mDNSu8 *ptr; + int i; + LargeCacheRecord lcr; + ResourceRecord *rr = &lcr.r.resrec; + + if (context->state == foundNS) + { + // we just found the NS record - look for the corresponding A record in the Additionals section + if (!msg->h.numAdditionals) return queryNSAddr(context); + ptr = LocateAdditionals(msg, end); + if (!ptr) + { + LogMsg("ERROR: lookupNSAddr - LocateAdditionals returned NULL, expected %d additionals", msg->h.numAdditionals); + return queryNSAddr(context); + } + else + { + for (i = 0; i < msg->h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) + { + LogMsg("ERROR: lookupNSAddr, Additionals - GetLargeResourceRecord returned NULL"); + return queryNSAddr(context); + } + if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, rr->name)) + { + context->addr = rr->rdata->u.ipv4; + context->state = foundA; + return smContinue; + } + } + } + // no A record in Additionals - query the server + return queryNSAddr(context); + } + else if (context->state == lookupA) + { + ptr = LocateAnswers(msg, end); + if (!ptr) { LogMsg("ERROR: lookupNSAddr: LocateAnswers returned NULL"); return smError; } + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { LogMsg("ERROR: lookupNSAddr, Answers - GetLargeResourceRecord returned NULL"); break; } + if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, rr->name)) + { + context->addr = rr->rdata->u.ipv4; + context->state = foundA; + return smContinue; + } + } + LogMsg("ERROR: lookupNSAddr: Address record not found in answer section"); + return smError; + } + else { LogMsg("ERROR: lookupNSAddr - bad state %d", context->state); return smError; } + } + +mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext *context, char *portName, mDNSIPPort *port) + { + int i; + LargeCacheRecord lcr; + const mDNSu8 *ptr; + DNSQuestion *q; + mStatus err; + + if (context->state == lookupPort) // we've already issued the query + { + if (!msg) { LogMsg("ERROR: hndlLookupUpdatePort - NULL message"); return smError; } + ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { LogMsg("ERROR: hndlLookupUpdatePort - GetLargeResourceRecord returned NULL"); return smError; } + if (ResourceRecordAnswersQuestion(&lcr.r.resrec, &context->question)) + { + *port = lcr.r.resrec.rdata->u.srv.port; + context->state = foundPort; + return smContinue; + } + } + debugf("hndlLookupUpdatePort - no answer for type %s", portName); + port->NotAnInteger = 0; + context->state = foundPort; + return smContinue; + } + + // query the server for the update port for the zone + context->state = lookupPort; + q = &context->question; + MakeDomainNameFromDNSNameString(&q->qname, portName); + AppendDomainName(&q->qname, &context->zone); + q->qtype = kDNSType_SRV; + q->qclass = kDNSClass_IN; + err = startInternalQuery(q, context->m, getZoneData, context); + context->questionActive = mDNStrue; + if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err); + return smBreak; // break from state machine until we receive another packet + } + +mDNSlocal smAction hndlLookupPorts(DNSMessage *msg, const mDNSu8 *end, ntaContext *context) + { + smAction action; + + if (context->findUpdatePort && !context->updatePort.NotAnInteger) + { + action = lookupDNSPort(msg, end, context, UPDATE_PORT_NAME, &context->updatePort); + if (action != smContinue) return action; + } + if (context->findLLQPort && !context->llqPort.NotAnInteger) + return lookupDNSPort(msg, end, context, LLQ_PORT_NAME, &context->llqPort); + + return smContinue; + } + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Truncation Handling +#endif + +typedef struct + { + DNSQuestion *question; + DNSMessage *reply; + mDNSu16 replylen; + int nread; + mDNS *m; + } tcpInfo_t; + +// issue queries over a conected socket +mDNSlocal void conQueryCallback(int sd, void *context, mDNSBool ConnectionEstablished) + { + mStatus err = 0; + char msgbuf[356]; // 96 (hdr) + 256 (domain) + 4 (class/type) + DNSMessage *msg; + mDNSu8 *end; + tcpInfo_t *info = (tcpInfo_t *)context; + DNSQuestion *question = info->question; + int n; + mDNS *m = info->m; + + mDNS_Lock(m); + + if (ConnectionEstablished) + { + // connection is established - send the message + msg = (DNSMessage *)&msgbuf; + err = constructQueryMsg(msg, &end, question); + if (err) { LogMsg("ERROR: conQueryCallback: constructQueryMsg - %ld", err); goto error; } + err = mDNSSendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, mDNSNULL); + question->LastQTime = mDNSPlatformTimeNow(m); + if (err) { debugf("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %ld", err); goto error; } + } + else + { + if (!info->nread) + { + // read msg len + mDNSu8 lenbuf[2]; + n = mDNSPlatformReadTCP(sd, lenbuf, 2); + if (n != 2) + { + LogMsg("ERROR:conQueryCallback - attempt to read message length failed (read returned %d)", n); + goto error; + } + info->replylen = (mDNSu16)((mDNSu16)lenbuf[0] << 8 | lenbuf[1]); + if (info->replylen < sizeof(DNSMessageHeader)) + { LogMsg("ERROR: conQueryCallback - length too short (%d bytes)", info->replylen); goto error; } + info->reply = umalloc(info->replylen); + if (!info->reply) { LogMsg("ERROR: conQueryCallback - malloc failed"); goto error; } + } + n = mDNSPlatformReadTCP(sd, ((char *)info->reply) + info->nread, info->replylen - info->nread); + if (n < 0) { LogMsg("ERROR: conQueryCallback - read returned %d", n); goto error; } + info->nread += n; + if (info->nread == info->replylen) + { + // Finished reading message; convert the integer parts which are in IETF byte-order (MSB first, LSB second) + DNSMessage *msg = info->reply; + mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; + msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + uDNS_ReceiveMsg(m, msg, (mDNSu8 *)msg + info->replylen, mDNSNULL, zeroIPPort, mDNSNULL, zeroIPPort, question->InterfaceID); + mDNSPlatformTCPCloseConnection(sd); + ufree(info->reply); + ufree(info); + } + } + + mDNS_Unlock(m); + return; + + error: + mDNSPlatformTCPCloseConnection(sd); + if (info->reply) ufree(info->reply); + ufree(info); + mDNS_Unlock(m); + } + +mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m) + { + mStatus connectionStatus; + uDNS_QuestionInfo *info = &question->uDNS_info; + int sd; + tcpInfo_t *context; + + if (!src) { LogMsg("hndlTruncatedAnswer: TCP DNS response had TC bit set: ignoring"); return; } + + context = (tcpInfo_t *)umalloc(sizeof(tcpInfo_t)); + if (!context) { LogMsg("ERROR: hndlTruncatedAnswer - memallocate failed"); return; } + ubzero(context, sizeof(tcpInfo_t)); + context->question = question; + context->m = m; + info->id = newMessageID(&m->uDNS_info); + + connectionStatus = mDNSPlatformTCPConnect(src, UnicastDNSPort, question->InterfaceID, conQueryCallback, context, &sd); + if (connectionStatus == mStatus_ConnEstablished) // manually invoke callback if connection completes + { + conQueryCallback(sd, context, mDNStrue); + return; + } + if (connectionStatus == mStatus_ConnPending) return; // callback will be automatically invoked when connection completes + LogMsg("hndlTruncatedAnswer: connection failed"); + uDNS_StopQuery(m, question); //!!!KRS can we really call this here? + } + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Dynamic Updates +#endif + +mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr) + { + DNSMessage msg; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSOpaque16 id; + uDNS_RegInfo *regInfo = &rr->uDNS_info; + mStatus err = mStatus_UnknownErr; + + id = newMessageID(u); + InitializeDNSMessage(&msg.h, id, UpdateReqFlags); + rr->uDNS_info.id = id; + + // set zone + ptr = putZone(&msg, ptr, end, ®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; + } + + 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; + } + + + +mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *n = rr->uDNS_info.NATinfo; + + switch (rr->uDNS_info.state) + { + case regState_NATMap: + // we're in the middle of a NAT traversal operation + rr->uDNS_info.NATinfo = mDNSNULL; + if (!n) LogMsg("uDNS_DeregisterRecord: no NAT info context"); + else FreeNATInfo(m, n); // cause response to outstanding request to be ignored. + // Note: normally here we're trying to determine our public address, + //in which case there is not state to be torn down. For simplicity, + //we allow other operations to expire. + rr->uDNS_info.state = regState_Unregistered; + break; + case regState_ExtraQueued: + rr->uDNS_info.state = regState_Unregistered; + break; + case regState_FetchingZoneData: + rr->uDNS_info.state = regState_Cancelled; + return mStatus_NoError; + case regState_Refresh: + case regState_Pending: + case regState_UpdatePending: + rr->uDNS_info.state = regState_DeregDeferred; + LogMsg("Deferring deregistration of record %##s until registration completes", rr->resrec.name->c); + return mStatus_NoError; + case regState_Registered: + case regState_DeregPending: + break; + case regState_DeregDeferred: + case regState_Cancelled: + LogMsg("Double deregistration of record %##s type %d", + rr->resrec.name->c, rr->resrec.rrtype); + return mStatus_UnknownErr; + case regState_Unregistered: + LogMsg("Requested deregistration of unregistered record %##s type %d", + rr->resrec.name->c, rr->resrec.rrtype); + return mStatus_UnknownErr; + case regState_NATError: + case regState_NoTarget: + LogMsg("ERROR: uDNS_DeregisterRecord called for record %##s with bad state %s", rr->resrec.name->c, rr->uDNS_info.state == regState_NoTarget ? "regState_NoTarget" : "regState_NATError"); + return mStatus_UnknownErr; + } + + if (rr->uDNS_info.state == regState_Unregistered) + { + // unlink and deliver memfree + + unlinkAR(&u->RecordRegistrations, rr); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return mStatus_NoError; + } + + rr->uDNS_info.NATinfo = mDNSNULL; + if (n) FreeNATInfo(m, n); + + SendRecordDeregistration(m, rr); + return mStatus_NoError; + } + +mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs) + { + mDNSu32 i; + domainname target; + uDNS_RegInfo *info = &srs->uDNS_info; + ServiceRecordSet **p = &m->uDNS_info.ServiceRegistrations; + while (*p && *p != srs) p=&(*p)->next; + if (*p) { LogMsg("uDNS_RegisterService: %p %##s already in list", srs, srs->RR_SRV.resrec.name->c); return(mStatus_AlreadyRegistered); } + ubzero(info, sizeof(*info)); + *p = srs; + srs->next = mDNSNULL; + + srs->RR_SRV.resrec.rroriginalttl = kWideAreaTTL; + srs->RR_TXT.resrec.rroriginalttl = kWideAreaTTL; + srs->RR_PTR.resrec.rroriginalttl = kWideAreaTTL; + for (i = 0; i < srs->NumSubTypes;i++) srs->SubTypes[i].resrec.rroriginalttl = kWideAreaTTL; + + info->lease = mDNStrue; + + srs->RR_SRV.resrec.rdata->u.srv.target.c[0] = 0; + if (!GetServiceTarget(&m->uDNS_info, &srs->RR_SRV, &target)) + { + // defer registration until we've got a target + debugf("uDNS_RegisterService - no target for %##s", srs->RR_SRV.resrec.name->c); + info->state = regState_NoTarget; + return mStatus_NoError; + } + + info->state = regState_FetchingZoneData; + return startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + +mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs) + { + uDNS_RegInfo *info = &srs->uDNS_info; + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSMessage msg; + mDNSOpaque16 id; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + mStatus err = mStatus_UnknownErr; + mDNSu32 i; + + id = newMessageID(u); + InitializeDNSMessage(&msg.h, id, UpdateReqFlags); + + // put zone + ptr = putZone(&msg, ptr, end, &info->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass)); + if (!ptr) { LogMsg("ERROR: SendServiceDeregistration - putZone"); goto error; } + + if (!(ptr = putDeleteAllRRSets(&msg, ptr, srs->RR_SRV.resrec.name))) goto error; // this deletes SRV, TXT, and Extras + if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_PTR.resrec))) goto error; + for (i = 0; i < srs->NumSubTypes; i++) + if (!(ptr = putDeletionRecord(&msg, ptr, &srs->SubTypes[i].resrec))) goto error; + + + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForName(u, srs->RR_SRV.resrec.name)); + if (err && err != mStatus_TransientErr) { debugf("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto error; } + + SetRecordRetry(m, &srs->RR_SRV, err); + info->id = id; + info->state = regState_DeregPending; + + return; + + error: + unlinkSRS(m, srs); + info->state = regState_Unregistered; + } + +mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs) + { + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + char *errmsg = "Unknown State"; + + // don't re-register with a new target following deregistration + srs->uDNS_info.SRVChanged = srs->uDNS_info.SRVUpdateDeferred = mDNSfalse; + + if (nat) + { + if (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy) + DeleteNATPortMapping(m, nat, srs); + nat->reg.ServiceRegistration = mDNSNULL; + srs->uDNS_info.NATinfo = mDNSNULL; + FreeNATInfo(m, nat); + } + + switch (srs->uDNS_info.state) + { + case regState_Unregistered: + debugf("uDNS_DeregisterService - service %##s not registered", srs->RR_SRV.resrec.name->c); + return mStatus_BadReferenceErr; + case regState_FetchingZoneData: + // let the async op complete, then terminate + srs->uDNS_info.state = regState_Cancelled; + return mStatus_NoError; // deliver memfree upon completion of async op + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // deregister following completion of in-flight operation + srs->uDNS_info.state = regState_DeregDeferred; + return mStatus_NoError; + case regState_DeregPending: + case regState_DeregDeferred: + case regState_Cancelled: + debugf("Double deregistration of service %##s", srs->RR_SRV.resrec.name->c); + return mStatus_NoError; + case regState_NATError: // not registered + case regState_NATMap: // not registered + case regState_NoTarget: // not registered + unlinkSRS(m, srs); + srs->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + srs->ServiceCallback(m, srs, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return mStatus_NoError; + case regState_Registered: + srs->uDNS_info.state = regState_DeregPending; + SendServiceDeregistration(m, srs); + return mStatus_NoError; + case regState_ExtraQueued: // only for record registrations + errmsg = "bad state (regState_ExtraQueued)"; + goto error; + } + + error: + LogMsg("Error, uDNS_DeregisterService: %s", errmsg); + return mStatus_BadReferenceErr; + } + +mDNSexport mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra) + { + mStatus err = mStatus_UnknownErr; + + extra->r.resrec.RecordType = kDNSRecordTypeShared; // don't want it to conflict with the service name + extra->r.RecordCallback = mDNSNULL; // don't generate callbacks for extra RRs + + if (sr->uDNS_info.state == regState_Registered || sr->uDNS_info.state == regState_Refresh) + err = uDNS_RegisterRecord(m, &extra->r); + else + { + err = SetupRecordRegistration(m, &extra->r); + extra->r.uDNS_info.state = regState_ExtraQueued; // %%% Is it okay to overwrite the previous uDNS_info.state? + } + + if (!err) + { + extra->next = sr->Extras; + sr->Extras = extra; + } + return err; + } + +mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + ServiceRecordSet *parent = mDNSNULL; + AuthRecord *rptr; + uDNS_RegInfo *info = &rr->uDNS_info; + regState_t *stateptr = mDNSNULL; + + // find the record in registered service list + for (parent = u->ServiceRegistrations; parent; parent = parent->next) + if (&parent->RR_TXT == rr) { stateptr = &parent->uDNS_info.state; break; } + + if (!parent) + { + // record not part of a service - check individual record registrations + for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next) + if (rptr == rr) { stateptr = &rr->uDNS_info.state; break; } + if (!rptr) goto unreg_error; + } + + switch(*stateptr) + { + case regState_DeregPending: + case regState_DeregDeferred: + case regState_Cancelled: + case regState_Unregistered: + // not actively registered + goto unreg_error; + + case regState_FetchingZoneData: + case regState_NATMap: + case regState_ExtraQueued: + case regState_NoTarget: + // change rdata directly since it hasn't been sent yet + if (info->UpdateRDCallback) info->UpdateRDCallback(m, rr, rr->resrec.rdata); + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); + rr->NewRData = mDNSNULL; + return mStatus_NoError; + + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // registration in-flight. queue rdata and return + if (info->QueuedRData && info->UpdateRDCallback) + // if unsent rdata is already queued, free it before we replace it + info->UpdateRDCallback(m, rr, info->QueuedRData); + info->QueuedRData = rr->NewRData; + info->QueuedRDLen = rr->newrdlength; + rr->NewRData = mDNSNULL; + return mStatus_NoError; + + case regState_Registered: + info->OrigRData = rr->resrec.rdata; + info->OrigRDLen = rr->resrec.rdlength; + info->InFlightRData = rr->NewRData; + info->InFlightRDLen = rr->newrdlength; + rr->NewRData = mDNSNULL; + *stateptr = regState_UpdatePending; + if (parent) SendServiceRegistration(m, parent); + else sendRecordRegistration(m, rr); + return mStatus_NoError; + + case regState_NATError: + LogMsg("ERROR: uDNS_UpdateRecord called for record %##s with bad state regState_NATError", rr->resrec.name->c); + return mStatus_UnknownErr; // states for service records only + } + + unreg_error: + LogMsg("Requested update of record %##s type %d, part of service not currently registered", + rr->resrec.name->c, rr->resrec.rrtype); + return mStatus_Invalid; + } + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Periodic Execution Routines +#endif + + +mDNSlocal mDNSs32 CheckNATMappings(mDNS *m, mDNSs32 timenow) + { + NATTraversalInfo *ptr = m->uDNS_info.NATTraversals; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + while (ptr) + { + NATTraversalInfo *cur = ptr; + ptr = ptr->next; + if (cur->op != NATOp_AddrRequest || cur->state != NATState_Established) // no refresh necessary for established Add requests + { + if (cur->retry - timenow < 0) + { + if (cur->state == NATState_Established) RefreshNATMapping(cur, m); + else if (cur->state == NATState_Request || cur->state == NATState_Refresh) + { + if (cur->ntries >= NATMAP_MAX_TRIES) cur->ReceiveResponse(cur, m, mDNSNULL, 0); // may invalidate "cur" + else SendNATMsg(cur, m); + } + } + else if (cur->retry - nextevent < 0) nextevent = cur->retry; + } + } + return nextevent; + } + +mDNSlocal mDNSs32 CheckQueries(mDNS *m, mDNSs32 timenow) + { + DNSQuestion *q; + uDNS_GlobalInfo *u = &m->uDNS_info; + LLQ_Info *llq; + mDNSs32 sendtime; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + DNSMessage msg; + mStatus err = mStatus_NoError; + mDNSu8 *end; + uDNS_QuestionInfo *info; + + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) + { + q = u->CurrentQuery; + info = &q->uDNS_info; + llq = info->llq; + + if (!info->internal && ((!q->LongLived && !info->Answered) || (llq && llq->state < LLQ_Established)) && + info->RestartTime + RESTART_GOODBYE_DELAY - timenow < 0) + { + // if we've been spinning on restart setup, and we have known answers, give goodbyes (they may be re-added later) + while (info->knownAnswers) + { + CacheRecord *cr = info->knownAnswers; + info->knownAnswers = info->knownAnswers->next; + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + q->QuestionCallback(m, q, &cr->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + ufree(cr); + if (q != u->CurrentQuery) { debugf("CheckQueries - question removed via callback."); break; } + } + } + if (q != u->CurrentQuery) continue; + + if (q->LongLived && llq->state != LLQ_Poll) + { + if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Established) + { + if (llq->retry - timenow < 0) + { + // sanity check to avoid packet flood bugs + if (!llq->retry) + LogMsg("ERROR: retry timer not set for LLQ %##s in state %d", q->qname.c, llq->state); + else if (llq->state == LLQ_Established || llq->state == LLQ_Refresh) + sendLLQRefresh(m, q, llq->origLease); + else if (llq->state == LLQ_InitialRequest) + startLLQHandshake(m, llq, mDNSfalse); + else if (llq->state == LLQ_SecondaryRequest) + sendChallengeResponse(m, q, mDNSNULL); + else if (llq->state == LLQ_Retry) + { llq->ntries = 0; startLLQHandshake(m, llq, mDNSfalse); } + } + else if (llq->retry - nextevent < 0) nextevent = llq->retry; + } + } + else + { + sendtime = q->LastQTime + q->ThisQInterval; + if (m->SuppressStdPort53Queries && + sendtime - m->SuppressStdPort53Queries < 0) // Don't allow sendtime to be earlier than SuppressStdPort53Queries + sendtime = m->SuppressStdPort53Queries; + if (sendtime - timenow < 0) + { + DNSServer *server = GetServerForName(&m->uDNS_info, &q->qname); + if (server) + { + if (server->teststate == DNSServer_Untested) + { + InitializeDNSMessage(&msg.h, newMessageID(&m->uDNS_info), uQueryFlags); + end = putQuestion(&msg, msg.data, msg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN); + } + else + err = constructQueryMsg(&msg, &end, q); + if (err) LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %##s", q->qname.c); + else + { + if (server->teststate != DNSServer_Failed) + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &server->addr, UnicastDNSPort, -1, mDNSNULL); + m->SuppressStdPort53Queries = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+99)/100); + q->LastQTime = timenow; + if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); // surpress syslog messages if we have no network + else if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = q->ThisQInterval * 2; // don't increase interval if send failed + } + } + } + else if (sendtime - nextevent < 0) nextevent = sendtime; + } + u->CurrentQuery = u->CurrentQuery->next; + } + return nextevent; + } + +mDNSlocal mDNSs32 CheckRecordRegistrations(mDNS *m, mDNSs32 timenow) + { + AuthRecord *rr; + uDNS_RegInfo *rInfo; + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + //!!!KRS list should be pre-sorted by expiration + for (rr = u->RecordRegistrations; rr; rr = rr->next) + { + rInfo = &rr->uDNS_info; + if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_UpdatePending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh) + { + if (rr->LastAPTime + rr->ThisAPInterval - timenow < 0) + { +#if MDNS_DEBUGMSGS + char *op = "(unknown operation)"; + if (rInfo->state == regState_Pending) op = "registration"; + else if (rInfo->state == regState_DeregPending) op = "deregistration"; + else if (rInfo->state == regState_Refresh) op = "refresh"; + debugf("Retransmit record %s %##s", op, rr->resrec.name->c); +#endif + //LogMsg("Retransmit record %##s", rr->resrec.name->c); + if (rInfo->state == regState_DeregPending) SendRecordDeregistration(m, rr); + else sendRecordRegistration(m, rr); + } + if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval; + } + if (rInfo->lease && rInfo->state == regState_Registered) + { + if (rInfo->expire - timenow < 0) + { + debugf("refreshing record %##s", rr->resrec.name->c); + rInfo->state = regState_Refresh; + sendRecordRegistration(m, rr); + } + if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire; + } + } + return nextevent; + } + +mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m, mDNSs32 timenow) + { + ServiceRecordSet *s = m->uDNS_info.ServiceRegistrations; + uDNS_RegInfo *rInfo; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + // Note: ServiceRegistrations list is in the order they were created; important for in-order event delivery + while (s) + { + ServiceRecordSet *srs = s; + // NOTE: Must advance s here -- SendServiceDeregistration may delete the object we're looking at, + // and then if we tried to do srs = srs->next at the end we'd be referencing a dead object + s = s->next; + + rInfo = &srs->uDNS_info; + if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh || rInfo->state == regState_UpdatePending) + { + if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - timenow < 0) + { +#if MDNS_DEBUGMSGS + char *op = "unknown"; + if (rInfo->state == regState_Pending) op = "registration"; + else if (rInfo->state == regState_DeregPending) op = "deregistration"; + else if (rInfo->state == regState_Refresh) op = "refresh"; + else if (rInfo->state == regState_UpdatePending) op = "txt record update"; + debugf("Retransmit service %s %##s", op, srs->RR_SRV.resrec.name->c); +#endif + if (rInfo->state == regState_DeregPending) { SendServiceDeregistration(m, srs); continue; } + else SendServiceRegistration (m, srs); + } + if (nextevent - srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval > 0) + nextevent = srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval; + } + + if (rInfo->lease && rInfo->state == regState_Registered) + { + if (rInfo->expire - timenow < 0) + { + debugf("refreshing service %##s", srs->RR_SRV.resrec.name->c); + rInfo->state = regState_Refresh; + SendServiceRegistration(m, srs); + } + if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire; + } + } + return nextevent; + } + +mDNSexport void uDNS_Execute(mDNS *const m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSs32 nexte, timenow = mDNSPlatformTimeNow(m); + + u->nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + if (u->DelaySRVUpdate && u->NextSRVUpdate - timenow < 0) + { + u->DelaySRVUpdate = mDNSfalse; + UpdateSRVRecords(m); + } + + nexte = CheckNATMappings(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + if (m->SuppressStdPort53Queries && m->timenow - m->SuppressStdPort53Queries >= 0) + m->SuppressStdPort53Queries = 0; // If suppression time has passed, clear it + + nexte = CheckQueries(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + nexte = CheckRecordRegistrations(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + nexte = CheckServiceRegistrations(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Startup, Shutdown, and Sleep +#endif + +// DeregisterActive causes active LLQs to be removed from the server, e.g. before sleep. Pass false +// following a location change, as the server will reject deletions from a source address different +// from the address on which the LLQ was created. + +mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive) + { + DNSQuestion *q; + LLQ_Info *llq; + for (q = m->uDNS_info.ActiveQueries; q; q = q->next) + { + llq = q->uDNS_info.llq; + if (q->LongLived && llq) + { + if (llq->state == LLQ_GetZoneInfo) + { + debugf("Marking %##s suspend-deferred", q->qname.c); + llq->state = LLQ_SuspendDeferred; // suspend once we're done getting zone info + } + else if (llq->state < LLQ_Suspended) + { + if (DeregisterActive && (llq->state == LLQ_Established || llq->state == LLQ_Refresh)) + { debugf("Deleting LLQ %##s", q->qname.c); sendLLQRefresh(m, q, 0); } + debugf("Marking %##s suspended", q->qname.c); + llq->state = LLQ_Suspended; + ubzero(llq->id, 8); + } + else if (llq->state == LLQ_Poll) { debugf("Marking %##s suspended-poll", q->qname.c); llq->state = LLQ_SuspendedPoll; } + if (llq->NATMap) llq->NATMap = mDNSfalse; // may not need nat mapping if we restart with new route + } + } + CheckForUnreferencedLLQMapping(m); + } + +mDNSlocal void RestartQueries(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSQuestion *q; + LLQ_Info *llqInfo; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) + { + q = u->CurrentQuery; + u->CurrentQuery = u->CurrentQuery->next; + llqInfo = q->uDNS_info.llq; + q->uDNS_info.RestartTime = timenow; + q->uDNS_info.Answered = mDNSfalse; + if (q->LongLived) + { + if (!llqInfo) { LogMsg("Error: RestartQueries - %##s long-lived with NULL info", q->qname.c); continue; } + if (llqInfo->state == LLQ_Suspended || llqInfo->state == LLQ_NatMapWait) + { + llqInfo->ntries = -1; + llqInfo->deriveRemovesOnResume = mDNStrue; + startLLQHandshake(m, llqInfo, mDNStrue); // we set defer to true since several events that may generate restarts often arrive in rapid succession, and this cuts unnecessary packets + } + else if (llqInfo->state == LLQ_SuspendDeferred) + llqInfo->state = LLQ_GetZoneInfo; // we never finished getting zone data - proceed as usual + else if (llqInfo->state == LLQ_SuspendedPoll) + { + // if we were polling, we may have had bad zone data due to firewall, etc. - refetch + llqInfo->ntries = 0; + llqInfo->deriveRemovesOnResume = mDNStrue; + llqInfo->state = LLQ_GetZoneInfo; + startGetZoneData(&q->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, llqInfo); + } + } + else { q->LastQTime = timenow; q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } // trigger poll in 1 second (to reduce packet rate when restarts come in rapid succession) + } + } + +mDNSexport void mDNS_UpdateLLQs(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + + mDNS_Lock(m); + if (u->LLQNatInfo) + { + DeleteNATPortMapping(m, u->LLQNatInfo, mDNSNULL); + FreeNATInfo(m, u->LLQNatInfo); // routine clears u->LLQNatInfo ptr + } + SuspendLLQs(m, mDNStrue); + RestartQueries(m); + mDNS_Unlock(m); + } + +// simplest sleep logic - rather than having sleep states that must be dealt with explicitly in all parts of +// the code, we simply send a deregistration, and put the service in Refresh state, with a timeout far enough +// in the future that we'll sleep (or the sleep will be cancelled) before it is retransmitted. Then to wake, +// we just move up the timers. + + + +mDNSlocal void SleepRecordRegistrations(mDNS *m) + { + DNSMessage msg; + AuthRecord *rr = m->uDNS_info.RecordRegistrations; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + + while (rr) + { + if (rr->uDNS_info.state == regState_Registered || + rr->uDNS_info.state == regState_Refresh) + { + mDNSu8 *ptr = msg.data, *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + InitializeDNSMessage(&msg.h, newMessageID(&m->uDNS_info), UpdateReqFlags); + + // construct deletion update + ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put zone"); return; } + ptr = putDeletionRecord(&msg, ptr, &rr->resrec); + if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put deletion record"); return; } + + mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForName(&m->uDNS_info, rr->resrec.name)); + rr->uDNS_info.state = regState_Refresh; + rr->LastAPTime = timenow; + rr->ThisAPInterval = 300 * mDNSPlatformOneSecond; + } + rr = rr->next; + } + } + +mDNSlocal void WakeRecordRegistrations(mDNS *m) + { + mDNSs32 timenow = mDNSPlatformTimeNow(m); + AuthRecord *rr = m->uDNS_info.RecordRegistrations; + + while (rr) + { + if (rr->uDNS_info.state == regState_Refresh) + { + // trigger slightly delayed refresh (we usually get this message before kernel is ready to send packets) + rr->LastAPTime = timenow; + rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL; + } + rr = rr->next; + } + } + +mDNSlocal void SleepServiceRegistrations(mDNS *m) + { + ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations; + while(srs) + { + uDNS_RegInfo *info = &srs->uDNS_info; + NATTraversalInfo *nat = info->NATinfo; + + if (nat) + { + if (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy) + DeleteNATPortMapping(m, nat, srs); + nat->reg.ServiceRegistration = mDNSNULL; + srs->uDNS_info.NATinfo = mDNSNULL; + FreeNATInfo(m, nat); + } + + if (info->state == regState_UpdatePending) + { + // act as if the update succeeded, since we're about to delete the name anyway + AuthRecord *txt = &srs->RR_TXT; + uDNS_RegInfo *txtInfo = &txt->uDNS_info; + info->state = regState_Registered; + // deallocate old RData + if (txtInfo->UpdateRDCallback) txtInfo->UpdateRDCallback(m, txt, txtInfo->OrigRData); + SetNewRData(&txt->resrec, txtInfo->InFlightRData, txtInfo->InFlightRDLen); + txtInfo->OrigRData = mDNSNULL; + txtInfo->InFlightRData = mDNSNULL; + } + + if (info->state == regState_Registered || info->state == regState_Refresh) + { + mDNSOpaque16 origid = srs->uDNS_info.id; + info->state = regState_DeregPending; // state expected by SendDereg() + SendServiceDeregistration(m, srs); + info->id = origid; + info->state = regState_NoTarget; // when we wake, we'll re-register (and optionally nat-map) once our address record completes + srs->RR_SRV.resrec.rdata->u.srv.target.c[0] = 0; + } + srs = srs->next; + } + } + +mDNSlocal void WakeServiceRegistrations(mDNS *m) + { + mDNSs32 timenow = mDNSPlatformTimeNow(m); + ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations; + while(srs) + { + if (srs->uDNS_info.state == regState_Refresh) + { + // trigger slightly delayed refresh (we usually get this message before kernel is ready to send packets) + srs->RR_SRV.LastAPTime = timenow; + srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL; + } + srs = srs->next; + } + } + +mDNSexport void uDNS_Init(mDNS *const m) + { + mDNSPlatformMemZero(&m->uDNS_info, sizeof(uDNS_GlobalInfo)); + m->uDNS_info.nextevent = m->timenow_last + 0x78000000; + } + +mDNSexport void uDNS_Sleep(mDNS *const m) + { + SuspendLLQs(m, mDNStrue); + SleepServiceRegistrations(m); + SleepRecordRegistrations(m); + } + +mDNSexport void uDNS_Wake(mDNS *const m) + { + RestartQueries(m); + WakeServiceRegistrations(m); + WakeRecordRegistrations(m); + } diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h new file mode 100644 index 0000000000..2a6a9edf2b --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h @@ -0,0 +1,192 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: uDNS.h,v $ +Revision 1.32.2.1 2006/08/29 06:24:23 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.32 2005/07/29 19:46:10 ksekar +<rdar://problem/4191860> reduce polling period on failed LLQs to 15 minutes + +Revision 1.31 2005/03/31 02:19:56 cheshire +<rdar://problem/4021486> Fix build warnings +Reviewed by: Scott Herscher + +Revision 1.30 2005/03/04 03:00:03 ksekar +<rdar://problem/4026546> Retransmissions happen too early, causing registrations to conflict with themselves + +Revision 1.29 2005/01/11 22:50:53 ksekar +Fixed constant naming (was using kLLQ_DefLease for update leases) + +Revision 1.28 2004/12/22 00:13:49 ksekar +<rdar://problem/3873993> Change version, port, and polling interval for LLQ + +Revision 1.27 2004/11/23 04:06:50 cheshire +Get rid of floating point constant -- in a small embedded device, bringing in all +the floating point libraries just to halve an integer value is a bit too heavyweight. + +Revision 1.26 2004/11/22 17:49:15 ksekar +Changed INIT_REFRESH from fraction to decimal + +Revision 1.25 2004/11/22 17:16:20 ksekar +<rdar://problem/3854298> Unicast services don't disappear when you disable all networking + +Revision 1.24 2004/11/19 04:24:08 ksekar +<rdar://problem/3682609> Security: Enforce a "window" on one-shot wide-area queries + +Revision 1.23 2004/11/18 18:04:21 ksekar +Add INIT_REFRESH constant + +Revision 1.22 2004/11/15 20:09:24 ksekar +<rdar://problem/3719050> Wide Area support for Add/Remove record + +Revision 1.21 2004/11/11 20:14:55 ksekar +<rdar://problem/3719574> Wide-Area registrations not deregistered on sleep + +Revision 1.20 2004/10/16 00:16:59 cheshire +<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check + +Revision 1.19 2004/09/17 01:08:49 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.18 2004/09/03 19:23:05 ksekar +<rdar://problem/3788460>: Need retransmission mechanism for wide-area service registrations + +Revision 1.17 2004/09/01 03:59:29 ksekar +<rdar://problem/3783453>: Conditionally compile out uDNS code on Windows + +Revision 1.16 2004/08/25 00:37:27 ksekar +<rdar://problem/3774635>: Cleanup DynDNS hostname registration code + +Revision 1.15 2004/07/30 17:40:06 ksekar +<rdar://problem/3739115>: TXT Record updates not available for wide-area services + +Revision 1.14 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.13 2004/07/29 02:03:35 ksekar +Delete unused #define and structure field + +Revision 1.12 2004/07/26 22:49:30 ksekar +<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client + +Revision 1.11 2004/06/17 01:13:11 ksekar +<rdar://problem/3696616>: polling interval too short + +Revision 1.10 2004/06/11 05:45:03 ksekar +<rdar://problem/3682397>: Change SRV names for LLQ/Update port lookups + +Revision 1.9 2004/06/01 23:46:50 ksekar +<rdar://problem/3675149>: DynDNS: dynamically look up LLQ/Update ports + +Revision 1.8 2004/05/28 23:42:37 ksekar +<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.7 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.6 2004/03/13 01:57:33 ksekar +<rdar://problem/3192546>: DynDNS: Dynamic update of service records + +Revision 1.5 2004/02/21 08:56:58 bradley +Wrap prototypes with extern "C" for C++ builds. + +Revision 1.4 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.3 2004/01/24 03:38:27 cheshire +Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.2 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.1 2003/12/13 03:05:27 ksekar +<rdar://problem/3192548>: DynDNS: Unicast query of service records + + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef __UDNS_H_ +#define __UDNS_H_ + +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#define RESTART_GOODBYE_DELAY (6 * mDNSPlatformOneSecond) // delay after restarting LLQ before nuking previous known answers (avoids flutter if we restart before we have networking up) +#define MIN_UCAST_PERIODIC_EXEC (5 * mDNSPlatformOneSecond) +#define INIT_UCAST_POLL_INTERVAL (3 * mDNSPlatformOneSecond) // this interval is used after send failures on network transitions + // which typically heal quickly, so we start agressively and exponentially back off +#define MAX_UCAST_POLL_INTERVAL (60 * 60 * mDNSPlatformOneSecond) +#define LLQ_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc. +#define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond) // require server responses within one minute of request +#define UPDATE_PORT_NAME "_dns-update._udp." +#define LLQ_PORT_NAME "_dns-llq._udp" +#define DEFAULT_UPDATE_LEASE 7200 + +// Entry points into unicast-specific routines + +extern mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question); +extern mDNSBool uDNS_IsActiveQuery(DNSQuestion *const question, uDNS_GlobalInfo *u); // returns true if OK to call StopQuery +extern mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question); + +extern void uDNS_Init(mDNS *const m); +extern void uDNS_Sleep(mDNS *const m); +extern void uDNS_Wake(mDNS *const m); +#define uDNS_Close uDNS_Sleep + +// uDNS_UpdateRecord +// following fields must be set, and the update validated, upon entry. +// rr->NewRData +// rr->newrdlength +// rr->UpdateCallback + +extern mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra); +extern mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr); + +extern mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr); +extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr); + +extern mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs); +extern mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs); + +// integer fields of msg header must be in HOST byte order before calling this routine +extern void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, + const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); + +extern void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len); + +// returns time of next scheduled event +extern void uDNS_Execute(mDNS *const m); + + +#ifdef __cplusplus + } +#endif + +#endif // __UDNS_H_ diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c new file mode 100644 index 0000000000..4351e0edde --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c @@ -0,0 +1,3695 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2006 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: uds_daemon.c,v $ +Revision 1.201.2.1 2006/08/29 06:24:36 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.201 2006/06/29 03:02:47 cheshire +<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support + +Revision 1.200 2006/06/28 08:56:26 cheshire +Added "_op" to the end of the operation code enum values, +to differentiate them from the routines with the same names + +Revision 1.199 2006/06/28 08:53:39 cheshire +Added (commented out) debugging messages + +Revision 1.198 2006/06/27 20:16:07 cheshire +Fix code layout + +Revision 1.197 2006/05/18 01:32:35 cheshire +<rdar://problem/4472706> iChat: Lost connection with Bonjour +(mDNSResponder insufficiently defensive against malformed browsing PTR responses) + +Revision 1.196 2006/05/05 07:07:13 cheshire +<rdar://problem/4538206> mDNSResponder fails when UDS reads deliver partial data + +Revision 1.195 2006/04/25 20:56:28 mkrochma +Added comment about previous checkin + +Revision 1.194 2006/04/25 18:29:36 mkrochma +Workaround for warning: unused variable 'status' when building mDNSPosix + +Revision 1.193 2006/03/19 17:14:38 cheshire +<rdar://problem/4483117> Need faster purging of stale records +read_rr_from_ipc_msg was not setting namehash and rdatahash + +Revision 1.192 2006/03/18 20:58:32 cheshire +Misplaced curly brace + +Revision 1.191 2006/03/10 22:19:43 cheshire +Update debugging message in resolve_result_callback() to indicate whether event is ADD or RMV + +Revision 1.190 2006/03/10 21:56:12 cheshire +<rdar://problem/4111464> After record update, old record sometimes remains in cache +When service TXT and SRV record both change, clients with active resolve calls get *two* callbacks, one +when the TXT data changes, and then immediately afterwards a second callback with the new port number +This change suppresses the first unneccessary (and confusing) callback + +Revision 1.189 2006/01/06 00:56:31 cheshire +<rdar://problem/4400573> Should remove PID file on exit + +Revision 1.188 2005/10/11 22:15:03 cheshire +<rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c +Only compile uds_validatelists() when building for Mac OS X + +Revision 1.187 2005/10/11 20:30:27 cheshire +<rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c + +Revision 1.186 2005/09/12 07:11:53 herscher +<rdar://problem/4248878> Lazily call RegisterSearchDomains to workaround crashes of several routers. This code is conditionally compiled, and currently is only enabled on Windows platforms. + +Revision 1.185 2005/07/29 00:55:10 ksekar +Removed validation check in uds_validatelists which generated false alarms + +Revision 1.184 2005/07/04 22:40:26 cheshire +Additional debugging code to help catch memory corruption + +Revision 1.183 2005/06/13 22:39:11 cheshire +<rdar://problem/4144870> Missing return statement in handle_enum_request() error handling + +Revision 1.182 2005/03/21 00:39:31 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.181 2005/03/20 20:21:32 shersche +<rdar://problem/4056827> mDNSResponder crashes when incorrect interface index is passed to DNSServiceRegister() +Text record length and data parameters must be initialized to 0 and NULL to ensure that the service request +object is cleaned up correctly when encountering an interface index error. + +Revision 1.180 2005/03/10 00:13:12 cheshire +<rdar://problem/4043098> DNSServiceBrowse no longer returning error codes for invalid types +In handle_browse_request(), mStatus err was being set correctly if an error occurred, +but the end of the function returned mStatus_NoError intead of err. + +Revision 1.179 2005/03/04 02:47:26 ksekar +<rdar://problem/4026393> SCPreference domains disappear from enumeration when moving out from firewall + +Revision 1.178 2005/02/25 19:35:38 ksekar +<rdar://problem/4023750> Non-local empty string registration failures should not return errors to caller + +Revision 1.177 2005/02/25 03:05:41 cheshire +Change "broken pipe" message to debugf() + +Revision 1.176 2005/02/24 18:44:45 ksekar +<rdar://problem/4018516> Printer Sharing does not get re-registered with wide-area + +Revision 1.175 2005/02/21 21:31:25 ksekar +<rdar://problem/4015162> changed LogMsg to debugf + +Revision 1.174 2005/02/20 01:41:17 cheshire +Fix compiler signed/unsigned warning + +Revision 1.173 2005/02/18 01:26:42 cheshire +<rdar://problem/4012162> "Could not write data to client after 60 seconds" message could be more helpful +Log additional information about failed client + +Revision 1.172 2005/02/18 00:58:35 cheshire +<rdar://problem/4012162> "Could not write data to client after 60 seconds" message could be more helpful + +Revision 1.171 2005/02/18 00:43:12 cheshire +<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long + +Revision 1.170 2005/02/16 01:15:02 cheshire +Improve LogOperation() debugging messages for DNSServiceBrowse and DNSServiceRegister + +Revision 1.169 2005/02/08 01:57:14 cheshire +More detailed error reporting in udsserver_init() + +Revision 1.168 2005/02/03 00:44:37 cheshire +<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL + +Revision 1.167 2005/02/02 02:19:32 cheshire +Add comment explaining why unlink(MDNS_UDS_SERVERPATH); fails + +Revision 1.166 2005/02/01 19:58:52 ksekar +Shortened cryptic "broken pipe" syslog message + +Revision 1.165 2005/02/01 19:56:47 ksekar +Moved LogMsg from daemon.c to uds_daemon.c, cleaned up wording + +Revision 1.164 2005/01/28 06:07:55 cheshire +Don't use deliver_error() from within handle_regrecord_request() + +Revision 1.163 2005/01/28 01:39:16 cheshire +Include file descriptor number in "broken pipe" message + +Revision 1.162 2005/01/27 23:59:20 cheshire +Remove extraneous LogMsg + +Revision 1.161 2005/01/27 22:57:56 cheshire +Fix compile errors on gcc4 + +Revision 1.160 2005/01/27 20:52:11 cheshire +<rdar://problem/3972566> mDNSResponder leaks sockets for add/update/remove record calls + +Revision 1.159 2005/01/27 01:45:25 cheshire +<rdar://problem/3976147> mDNSResponder should never call exit(1); + +Revision 1.158 2005/01/25 17:28:07 ksekar +<rdar://problem/3971467> Should not return "local" twice for domain enumeration + +Revision 1.157 2005/01/21 02:20:39 cheshire +Fix mistake in LogOperation() format string + +Revision 1.156 2005/01/19 19:15:36 ksekar +Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer + +Revision 1.155 2005/01/19 03:00:47 cheshire +Show Add/Rmv in DNSServiceBrowse LogOperation() message + +Revision 1.154 2005/01/15 00:56:42 ksekar +<rdar://problem/3954575> Unicast services don't disappear when logging +out of VPN + +Revision 1.153 2005/01/14 18:44:28 ksekar +<rdar://problem/3954609> mDNSResponder is crashing when changing domains + +Revision 1.152 2005/01/13 17:16:38 ksekar +Back out checkin 1.150 - correct fix is on clientstub side + +Revision 1.151 2005/01/11 21:06:29 ksekar +Changed now-benign LogMsg to debugf + +Revision 1.150 2005/01/07 23:59:15 ksekar +<rdar://problem/3942900> dnd-sd shows the wrong port numbers + +Revision 1.149 2004/12/20 23:20:35 cheshire +<rdar://problem/3928361> mDNSResponder crashes repeatedly when printer sharing is enabled +Make sure to call mDNS_SetupResourceRecord() for all newly created AuthRecords + +Revision 1.148 2004/12/20 20:37:35 cheshire +AllowRemoteQuery not set for the extras in a ServiceRecordSet + +Revision 1.147 2004/12/20 00:15:41 cheshire +Include client file descriptor numbers in udsserver_info() output + +Revision 1.146 2004/12/17 05:25:47 cheshire +<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs + +Revision 1.145 2004/12/16 21:39:46 cheshire +Include CacheGroup objects in CacheUsed count + +Revision 1.144 2004/12/16 21:27:38 ksekar +Fixed build failures when compiled with verbose debugging messages + +Revision 1.143 2004/12/16 20:13:02 cheshire +<rdar://problem/3324626> Cache memory management improvements + +Revision 1.142 2004/12/16 08:07:33 shersche +Fix compiler error (mixed declarations and code) on Windows + +Revision 1.141 2004/12/16 01:56:21 cheshire +Improve DNSServiceEnumerateDomains syslog message + +Revision 1.140 2004/12/14 03:02:10 ksekar +<rdar://problem/3919016> Rare race condition can cause crash + +Revision 1.139 2004/12/13 21:18:45 ksekar +Include uDNS registrations in CountPeerRegistrations + +Revision 1.138 2004/12/13 18:23:18 ksekar +<rdar://problem/3915805> mDNSResponder error when quitting iChat - +don't close sockets delivering errors to blocked clients + +Revision 1.137 2004/12/13 00:09:22 ksekar +<rdar://problem/3915805> mDNSResponder error when quitting iChat + +Revision 1.136 2004/12/11 01:52:10 cheshire +<rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too + +Revision 1.135 2004/12/10 20:46:37 cheshire +Change LogOperation message to debugf + +Revision 1.134 2004/12/10 13:19:37 cheshire +Add verbosedebugf() logging message in CountPeerRegistrations() + +Revision 1.133 2004/12/10 05:27:26 cheshire +<rdar://problem/3909147> Guard against multiple autoname services of the same type on the same machine + +Revision 1.132 2004/12/10 04:28:28 cheshire +<rdar://problem/3914406> User not notified of name changes for services using new UDS API + +Revision 1.131 2004/12/10 02:09:25 cheshire +<rdar://problem/3898376> Modify default TTLs + +Revision 1.130 2004/12/10 00:55:24 cheshire +Add full name and type to LogOperation messages for DNSServiceAddRecord/UpdateRecord/RemoveRecord + +Revision 1.129 2004/12/09 03:17:23 ksekar +<rdar://problem/3910435> DomainEnumeration interface index should be zero + +Revision 1.128 2004/12/07 21:26:05 ksekar +<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration + +Revision 1.127 2004/12/07 20:42:34 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.126 2004/12/07 17:23:55 ksekar +Fixed LogOperation + +Revision 1.125 2004/12/06 21:15:23 ksekar +<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.124 2004/11/30 02:19:14 cheshire +<rdar://problem/3827971> Raise maxfds.rlim_cur for mDNSResponder + +Revision 1.123 2004/11/29 23:50:57 cheshire +Checkin 1.122 not necessary + +Revision 1.122 2004/11/24 17:55:01 ksekar +Added log message clarifying <rdar://problem/3869241> For unicast operations, verify that service types are legal + +Revision 1.121 2004/11/24 04:45:52 cheshire +Spelling mistake + +Revision 1.120 2004/11/24 00:10:44 cheshire +<rdar://problem/3869241> For unicast operations, verify that service types are legal + +Revision 1.119 2004/11/23 23:54:17 ksekar +<rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures +can crash mDNSResponder + +Revision 1.118 2004/11/23 22:33:01 cheshire +<rdar://problem/3654910> Remove temporary workaround code for iChat + +Revision 1.117 2004/11/23 20:23:10 ksekar +Fixed LogOperation that causes crash on connected service deregistrations + +Revision 1.116 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.115 2004/11/13 00:12:53 ksekar +Fixed some LogOperation printf converstions for debug builds. + +Revision 1.114 2004/11/12 18:25:45 shersche +Tidy up cross platform usleep code fragment. + +Revision 1.113 2004/11/12 03:21:41 rpantos +rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + +Revision 1.112 2004/11/11 16:58:32 ksekar +Removed unused code (previously wrapped in #if 0) + +Revision 1.111 2004/11/05 22:47:37 shersche +Conditionally compile usleep(1000) to be Sleep(1) on Windows +Submitted by: Pavel Repin <prepin@gmail.com> + +Revision 1.110 2004/11/05 19:56:56 ksekar +<rdar://problem/3862646> We no longer need to browse .Mac domains by +default - changed #if 0 to more descriptive #ifdef _HAVE_SETDOMAIN_SUPPORT_ + +Revision 1.109 2004/11/04 03:40:45 cheshire +More debugging messages + +Revision 1.108 2004/11/03 02:25:51 cheshire +<rdar://problem/3324137> Conflict for Computer Name should update *all* empty string services, not just the one with the conflict + +Revision 1.107 2004/11/02 19:39:23 ksekar +<rdar://problem/3862646> We no longer need to browse .Mac domains by default + +Revision 1.106 2004/11/02 02:12:21 cheshire +<rdar://problem/3839111> Remove unnecessary memory allocations + +Revision 1.105 2004/10/28 19:07:19 cheshire +Add some more debugging checks and improved LogOperation() messages + +Revision 1.104 2004/10/26 18:53:15 cheshire +Avoid unused variable warning + +Revision 1.103 2004/10/26 07:15:55 cheshire +Add file descriptor number to all LogOperation messages + +Revision 1.102 2004/10/26 06:11:42 cheshire +Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed + +Revision 1.101 2004/10/26 04:31:44 cheshire +Rename CountSubTypes() as ChopSubTypes() + +Revision 1.100 2004/10/26 01:17:48 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.99 2004/10/19 21:33:22 cheshire +<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.98 2004/10/14 01:59:33 cheshire +<rdar://problem/3839208> UDS resolves don't work for uDNS services + +Revision 1.97 2004/10/13 00:58:35 cheshire +<rdar://problem/3832738> Registering a proxy doesn't work + +Revision 1.96 2004/09/30 00:25:00 ksekar +<rdar://problem/3695802> Dynamically update default registration domains on config change + +Revision 1.95 2004/09/26 23:20:36 ksekar +<rdar://problem/3813108> Allow default registrations in multiple wide-area domains + +Revision 1.94 2004/09/22 18:27:06 ksekar +<rdar://problem/3811427> allow DNSServiceAddRecord to pass zero to get +default record TTL + +Revision 1.93 2004/09/22 02:39:44 cheshire +<rdar://problem/3810757> Allow DNSServiceRegisterRecord to pass zero to get default record TTL + +Revision 1.92 2004/09/22 02:34:04 cheshire +Rename parameter "ttl" to "GetTTL" for clarity + +Revision 1.91 2004/09/22 02:25:43 cheshire +Fix spelling errors + +Revision 1.90 2004/09/21 23:40:12 ksekar +<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure + +Revision 1.89 2004/09/21 23:29:51 cheshire +<rdar://problem/3680045> DNSServiceResolve should delay sending packets + +Revision 1.88 2004/09/21 23:12:46 cheshire +Reorder initialization of question fields to match structure order + +Revision 1.87 2004/09/21 22:18:33 cheshire +In SIGINFO output, display a '-' next to records that have the Unique bit set + +Revision 1.86 2004/09/21 21:05:11 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.85 2004/09/18 01:11:58 ksekar +<rdar://problem/3806734> Add a user's default domain to empty-string browse list + +Revision 1.84 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.83 2004/09/16 23:26:33 cheshire +Move version check inside preceeding "if" that checks we have a complete header + +Revision 1.82 2004/09/16 23:14:25 cheshire +Changes for Windows compatibility + +Revision 1.81 2004/09/16 21:46:38 ksekar +<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain + +Revision 1.80 2004/09/16 01:58:23 cheshire +Fix compiler warnings + +Revision 1.79 2004/09/16 00:24:49 cheshire +<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.78 2004/09/15 21:44:20 cheshire +<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init +Show time value in log to help diagnose errors + +Revision 1.77 2004/09/15 00:19:18 cheshire +<rdar://problem/3785823> read_rr_from_ipc_msg should use mDNS_SetupResourceRecord() + +Revision 1.76 2004/09/02 06:39:52 cheshire +Minor textual cleanup for clarity + +Revision 1.75 2004/09/02 03:48:47 cheshire +<rdar://problem/3709039> Disable targeted unicast query support by default +1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record +2. New field AllowRemoteQuery in AuthRecord structure +3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set +4. mDNS.c only answers remote queries if AllowRemoteQuery is set + +Revision 1.74 2004/08/25 02:32:47 cheshire +Minor cleanup: replace "®type[0]" with "regtype" + +Revision 1.73 2004/08/25 02:30:40 cheshire +<rdar://problem/3588761> Current method of doing subtypes causes name collisions + +Revision 1.72 2004/08/14 03:22:42 cheshire +<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.71 2004/08/11 04:21:21 rpantos +Fix Windows build. + +Revision 1.70 2004/08/11 02:07:00 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init() +Move CheckForDuplicateRegistrations from daemon.c +<rdar://problem/3501938> No warning when accidentally registering the same service multiple times using socket API + +Revision 1.69 2004/08/10 16:14:48 cheshire +Fix debug builds (oops) + +Revision 1.68 2004/08/10 06:24:56 cheshire +Use types with precisely defined sizes for 'op' and 'reg_index', for better +compatibility if the daemon and the client stub are built using different compilers + +Revision 1.67 2004/07/27 07:14:16 shersche +make error socket non-blocking after call to connect() + +Revision 1.66 2004/07/13 21:24:25 rpantos +Fix for <rdar://problem/3701120>. + +Revision 1.65 2004/06/26 03:17:14 shersche +implement cross-platform strerror function + +Submitted by: herscher + +Revision 1.64 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.63 2004/06/24 03:43:44 rpantos +Fix previous checkin so it builds on Windows. + +Revision 1.62 2004/06/24 00:57:08 ksekar +Replaced code acccidentally removed in checkin 1.59. + +Revision 1.61 2004/06/19 00:09:39 cheshire +Remove unused strsep() implementation + +Revision 1.60 2004/06/18 19:10:00 cheshire +<rdar://problem/3588761> Current method of doing subtypes causes name collisions + +Revision 1.59 2004/06/18 05:10:31 rpantos +Changes to allow code to be used on Windows + +Revision 1.58 2004/06/15 03:54:08 cheshire +Include mDNS_TimeNow(&mDNSStorage) in SIGINFO output + +Revision 1.57 2004/06/12 01:47:27 ksekar +<rdar://problem/3690241>: BBEdit crashes when trying to check for newer version +udsserver_idle compared time in ticks to interval in seconds. + +Revision 1.56 2004/06/12 01:35:47 cheshire +Changes for Windows compatibility + +Revision 1.55 2004/06/05 00:04:27 cheshire +<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration + +Revision 1.54 2004/06/01 22:22:52 ksekar +<rdar://problem/3668635>: wide-area default registrations should be in +.local too + +Revision 1.53 2004/05/28 23:42:37 ksekar +<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.52 2004/05/26 00:39:49 ksekar +<rdar://problem/3667105>: wide-area DNS-SD servers don't appear in +Finder +Use local-only InterfaceID for GetDomains calls for sockets-API + +Revision 1.51 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.50 2004/05/14 16:39:47 ksekar +Browse for iChat locally for now. + +Revision 1.49 2004/05/13 21:33:52 ksekar +Clean up non-local registration control via config file. Force iChat +registrations to be local for now. + +Revision 1.48 2004/05/13 04:13:19 ksekar +Updated SIGINFO handler for multi-domain browses + +Revision 1.47 2004/05/12 22:04:01 ksekar +Implemented multi-domain browsing by default for uds_daemon. + +Revision 1.46 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +<rdar://problem/3592068>: Remove flags with zero value +<rdar://problem/3479569>: Passing in NULL causes a crash. + +Revision 1.45 2004/03/12 08:49:28 cheshire +#include <sys/socket.h> + +Revision 1.44 2004/02/25 01:25:27 ksekar +<rdar://problem/3569212>: DNSServiceRegisterRecord flags not error-checked + +Revision 1.43 2004/02/24 01:46:40 cheshire +Manually reinstate lost checkin 1.36 + +Revision 1.42 2004/02/05 19:39:29 cheshire +Move creation of /var/run/mDNSResponder.pid to uds_daemon.c, +so that all platforms get this functionality + +Revision 1.41 2004/02/03 18:59:02 cheshire +Change "char *domain" parameter for format_enumeration_reply to "const char *domain" + +Revision 1.40 2004/01/28 03:41:00 cheshire +<rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries + +Revision 1.39 2004/01/25 00:03:21 cheshire +Change to use mDNSVal16() instead of private PORT_AS_NUM() macro + +Revision 1.38 2004/01/19 19:51:46 cheshire +Fix compiler error (mixed declarations and code) on some versions of Linux + +Revision 1.37 2003/12/08 21:11:42 rpantos +Changes necessary to support mDNSResponder on Linux. + +Revision 1.36 2003/12/04 23:40:57 cheshire +<rdar://problem/3484766>: Security: Crashing bug in mDNSResponder +Fix some more code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + +Revision 1.35 2003/12/03 19:10:22 ksekar +<rdar://problem/3498644>: malloc'd data not zero'd + +Revision 1.34 2003/12/03 02:00:01 ksekar +<rdar://problem/3498644>: malloc'd data not zero'd + +Revision 1.33 2003/11/22 01:18:46 ksekar +<rdar://problem/3486646>: config change handler not called for dns-sd services + +Revision 1.32 2003/11/20 21:46:12 ksekar +<rdar://problem/3486635>: leak: DNSServiceRegisterRecord + +Revision 1.31 2003/11/20 20:33:05 ksekar +<rdar://problem/3486635>: leak: DNSServiceRegisterRecord + +Revision 1.30 2003/11/20 02:10:55 ksekar +<rdar://problem/3486643>: cleanup DNSServiceAdd/RemoveRecord + +Revision 1.29 2003/11/14 21:18:32 cheshire +<rdar://problem/3484766>: Security: Crashing bug in mDNSResponder +Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + +Revision 1.28 2003/11/08 22:18:29 cheshire +<rdar://problem/3477870>: Don't need to show process ID in *every* mDNSResponder syslog message + +Revision 1.27 2003/11/05 22:44:57 ksekar +<rdar://problem/3335230>: No bounds checking when reading data from client +Reviewed by: Stuart Cheshire + +Revision 1.26 2003/10/23 17:51:04 ksekar +<rdar://problem/3335216>: handle blocked clients more efficiently +Changed gettimeofday() to mDNS_TimeNow(&mDNSStorage); + +Revision 1.25 2003/10/22 23:37:49 ksekar +<rdar://problem/3459141>: crash/hang in abort_client + +Revision 1.24 2003/10/21 20:59:40 ksekar +<rdar://problem/3335216>: handle blocked clients more efficiently + +Revision 1.23 2003/09/23 02:12:43 cheshire +Also include port number in list of services registered via new UDS API + +Revision 1.22 2003/08/19 16:03:55 ksekar +<rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord +Check termination_context for NULL before dereferencing. + +Revision 1.21 2003/08/19 05:39:43 cheshire +<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord + +Revision 1.20 2003/08/16 03:39:01 cheshire +<rdar://problem/3338440> InterfaceID -1 indicates "local only" + +Revision 1.19 2003/08/15 20:16:03 cheshire +<rdar://problem/3366590> mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.18 2003/08/15 00:38:00 ksekar +<rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client + +Revision 1.17 2003/08/14 02:18:21 cheshire +<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.16 2003/08/13 23:58:52 ksekar +<rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration +Fixed pointer increment error, moved subtype reading for-loop for easier error bailout. + +Revision 1.15 2003/08/13 17:30:33 ksekar +<rdar://problem/3374671>: DNSServiceAddRecord doesn't work +Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords. + +Revision 1.14 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#if defined(_WIN32) +#include <process.h> +#define MDNS_LAZY_REGISTER_SEARCH_DOMAINS +#define dnssd_strerror(X) win32_strerror(X) +#define usleep(X) Sleep(((X)+999)/1000) +static char * win32_strerror(int inErrorCode); +#else +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#define dnssd_strerror(X) strerror(X) +#endif + +#include <stdlib.h> +#include <stdio.h> +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" +#include "uds_daemon.h" +#include "dns_sd.h" +#include "dnssd_ipc.h" + +// Apple specific configuration functionality, not required for other platforms +#ifdef __MACOSX__ +#include <sys/ucred.h> +#ifndef LOCAL_PEERCRED +#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */ +#endif // LOCAL_PEERCRED +#endif //__MACOSX__ + +#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS) +extern mStatus dDNS_RegisterSearchDomains( mDNS * const m ); +#endif + +// Types and Data Structures +// ---------------------------------------------------------------------- + +typedef enum + { + t_uninitialized, + t_morecoming, + t_complete, + t_error, + t_terminated + } transfer_state; + +typedef void (*req_termination_fn)(void *); + +typedef struct registered_record_entry + { + uint32_t key; + AuthRecord *rr; + struct registered_record_entry *next; + client_context_t client_context; + struct request_state *rstate; + } registered_record_entry; + +// A single registered service: ServiceRecordSet + bookkeeping +// Note that we duplicate some fields from parent service_info object +// to facilitate cleanup, when instances and parent may be deallocated at different times. +typedef struct service_instance + { + struct service_instance *next; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if this client wants us to automatically rename on conflict + mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? + mDNSBool rename_on_memfree; // Set on config change when we deregister original name + domainlabel name; + domainname domain; + mDNSBool default_local; // is this the "local." from an empty-string registration? + struct request_state *request; + dnssd_sock_t sd; + AuthRecord *subtypes; + ServiceRecordSet srs; // note - must be last field in struct + } service_instance; + +// A client-created service. May reference several service_info objects if default +// settings cause registration in multiple domains. +typedef struct + { + uint16_t txtlen; + void *txtdata; + mDNSIPPort port; + domainlabel name; + char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; + domainname type; + mDNSBool default_domain; + domainname host; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if this client wants us to automatically rename on conflict + mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? + int num_subtypes; + mDNSInterfaceID InterfaceID; + service_instance *instances; + struct request_state *request; + } service_info; + +// for multi-domain default browsing +typedef struct browser_t + { + DNSQuestion q; + domainname domain; + struct browser_t *next; + } browser_t; + +// parent struct for browser instances: list pointer plus metadata +typedef struct + { + mDNSBool default_domain; + mDNSBool ForceMCast; + domainname regtype; + mDNSInterfaceID interface_id; + struct request_state *rstate; + browser_t *browsers; + } browser_info_t; + +typedef struct + { + mStatus err; // Note: This field is in NETWORK byte order + int nwritten; + dnssd_sock_t sd; + } undelivered_error_t; + +typedef struct request_state + { + // connection structures + dnssd_sock_t sd; + + // state of read (in case message is read over several recv() calls) + transfer_state ts; + uint32_t hdr_bytes; // bytes of header already read + ipc_msg_hdr hdr; + uint32_t data_bytes; // bytes of message data already read + char *msgbuf; // pointer to data storage to pass to free() + char *msgdata; // pointer to data to be read from (may be modified) + int bufsize; // size of data storage + + // reply, termination, error, and client context info + int no_reply; // don't send asynchronous replies to client + int time_blocked; // record time of a blocked client + void *client_context; // don't touch this - pointer only valid in client's addr space + struct reply_state *replies; // corresponding (active) reply list + undelivered_error_t *u_err; + void *termination_context; + req_termination_fn terminate; + + //!!!KRS toss these pointers in a union + // registration context associated with this request (null if not applicable) + registered_record_entry *reg_recs; // muliple registrations for a connection-oriented request + service_info *service_registration; + browser_info_t *browser_info; + struct request_state *next; + } request_state; + +// struct physically sits between ipc message header and call-specific fields in the message buffer +typedef struct + { + DNSServiceFlags flags; // Note: This field is in NETWORK byte order + uint32_t ifi; // Note: This field is in NETWORK byte order + DNSServiceErrorType error; // Note: This field is in NETWORK byte order + } reply_hdr; + +typedef struct reply_state + { + // state of the transmission + dnssd_sock_t sd; + transfer_state ts; + uint32_t nwriten; + uint32_t len; + // context of the reply + struct request_state *request; // the request that this answers + struct reply_state *next; // if there are multiple unsent replies + // pointer into message buffer - allows fields to be changed after message is formatted + ipc_msg_hdr *mhdr; + reply_hdr *rhdr; + char *sdata; // pointer to start of call-specific data + // pointer to malloc'd buffer + char *msgbuf; + } reply_state; + +// domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected +// structures to handle callbacks +typedef struct + { + DNSQuestion question; + mDNS_DomainType type; + request_state *rstate; + } domain_enum_t; + +typedef struct + { + domain_enum_t *all; + domain_enum_t *def; + request_state *rstate; + } enum_termination_t; + +typedef struct + { + request_state *rstate; + DNSQuestion qtxt; + DNSQuestion qsrv; + // const ResourceRecord *txt; + // const ResourceRecord *srv; + mDNSBool srv; + mDNSBool txt; + rdataSRV srvdata; + mDNSu16 txtlen; + mDNSu8 txtdata[AbsoluteMaxDNSMessageData]; + } resolve_termination_t; + +#ifdef _HAVE_SETDOMAIN_SUPPORT_ +typedef struct default_browse_list_t + { + struct default_browse_list_t *next; + uid_t uid; + AuthRecord ptr_rec; + } default_browse_list_t; + +static default_browse_list_t *default_browse_list = NULL; +#endif // _HAVE_SETDOMAIN_SUPPORT_ + +// globals +mDNSexport mDNS mDNSStorage; +#define gmDNS (&mDNSStorage) + +static dnssd_sock_t listenfd = dnssd_InvalidSocket; +static request_state * all_requests = NULL; + +#define MAX_TIME_BLOCKED 60 * mDNSPlatformOneSecond // try to send data to a blocked client for 60 seconds before + // terminating connection +#define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee + // n get_string() calls w/o buffer overrun +// private function prototypes +mDNSlocal void connect_callback(void *info); +mDNSlocal int read_msg(request_state *rs); +mDNSlocal int send_msg(reply_state *rs); +mDNSlocal void abort_request(request_state *rs); +mDNSlocal void request_callback(void *info); +mDNSlocal void handle_resolve_request(request_state *rstate); +mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +mDNSlocal void question_termination_callback(void *context); +mDNSlocal void handle_browse_request(request_state *request); +mDNSlocal void browse_termination_callback(void *context); +mDNSlocal void handle_regservice_request(request_state *request); +mDNSlocal void regservice_termination_callback(void *context); +mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result); +mDNSlocal mStatus handle_add_request(request_state *rstate); +mDNSlocal mStatus handle_update_request(request_state *rstate); +mDNSlocal void append_reply(request_state *req, reply_state *rep); +mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain); +mDNSlocal void enum_termination_callback(void *context); +mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +mDNSlocal void handle_query_request(request_state *rstate); +mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err); +mDNSlocal void handle_enum_request(request_state *rstate); +mDNSlocal mStatus handle_regrecord_request(request_state *rstate); +mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result); +mDNSlocal void connected_registration_termination(void *context); +mDNSlocal void handle_reconfirm_request(request_state *rstate); +mDNSlocal AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flags); +mDNSlocal mStatus handle_removerecord_request(request_state *rstate); +mDNSlocal void reset_connected_rstate(request_state *rstate); +mDNSlocal int deliver_error(request_state *rstate, mStatus err); +mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err); +mDNSlocal transfer_state send_undelivered_error(request_state *rs); +mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request); +mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd); +mDNSlocal void my_perror(char *errmsg); +mDNSlocal void unlink_request(request_state *rs); +mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +mDNSlocal void resolve_termination_callback(void *context); +mDNSlocal int validate_message(request_state *rstate); +mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv); +mDNSlocal mStatus remove_record(request_state *rstate); +mDNSlocal void free_service_instance(service_instance *srv); +mDNSlocal uint32_t dnssd_htonl(uint32_t l); +mDNSlocal void handle_setdomain_request(request_state *rstate); + +// initialization, setup/teardown functions + +// If a platform specifies its own PID file name, we use that +#ifndef PID_FILE +#define PID_FILE "/var/run/mDNSResponder.pid" +#endif + +mDNSlocal void LogClientInfo(request_state *req) + { + void *t = req->termination_context; + if (t) + { + if (req->terminate == regservice_termination_callback) + { + service_instance *ptr; + for (ptr = ((service_info *)t)->instances; ptr; ptr = ptr->next) + LogMsgNoIdent("%3d: DNSServiceRegister %##s %u", req->sd, ptr->srs.RR_SRV.resrec.name->c, SRS_PORT(&ptr->srs)); + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + for (blist = req->browser_info->browsers; blist; blist = blist->next) + LogMsgNoIdent("%3d: DNSServiceBrowse %##s", req->sd, blist->q.qname.c); + } + else if (req->terminate == resolve_termination_callback) + LogMsgNoIdent("%3d: DNSServiceResolve %##s", req->sd, ((resolve_termination_t *)t)->qsrv.qname.c); + else if (req->terminate == question_termination_callback) + LogMsgNoIdent("%3d: DNSServiceQueryRecord %##s", req->sd, ((DNSQuestion *) t)->qname.c); + else if (req->terminate == enum_termination_callback) + LogMsgNoIdent("%3d: DNSServiceEnumerateDomains %##s", req->sd, ((enum_termination_t *) t)->all->question.qname.c); + } + } + +mDNSlocal void FatalError(char *errmsg) + { + LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno())); + *(long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does + abort(); // On platforms where writing to zero doesn't generate an exception, abort instead + } + +int udsserver_init(void) + { + dnssd_sockaddr_t laddr; + int ret; +#if defined(_WIN32) + u_long opt = 1; +#endif + + // If a particular platform wants to opt out of having a PID file, define PID_FILE to be "" + if (PID_FILE[0]) + { + FILE *fp = fopen(PID_FILE, "w"); + if (fp != NULL) + { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } + } + + if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) == dnssd_InvalidSocket) + { + my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed"); + goto error; + } + + bzero(&laddr, sizeof(laddr)); + + #if defined(USE_TCP_LOOPBACK) + { + laddr.sin_family = AF_INET; + laddr.sin_port = htons(MDNS_TCP_SERVERPORT); + laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); + if (ret < 0) + { + my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed"); + goto error; + } + } + #else + { + mode_t mask = umask(0); + unlink(MDNS_UDS_SERVERPATH); //OK if this fails + laddr.sun_family = AF_LOCAL; + #ifndef NOT_HAVE_SA_LEN + // According to Stevens (section 3.2), there is no portable way to + // determine whether sa_len is defined on a particular platform. + laddr.sun_len = sizeof(struct sockaddr_un); + #endif + strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH); + ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); + umask(mask); + if (ret < 0) + { + my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed"); + goto error; + } + } + #endif + + #if defined(_WIN32) + // + // SEH: do we even need to do this on windows? this socket + // will be given to WSAEventSelect which will automatically + // set it to non-blocking + // + if (ioctlsocket(listenfd, FIONBIO, &opt) != 0) + #else + if (fcntl(listenfd, F_SETFL, O_NONBLOCK) != 0) + #endif + { + my_perror("ERROR: could not set listen socket to non-blocking mode"); + goto error; + } + + if (listen(listenfd, LISTENQ) != 0) + { + my_perror("ERROR: could not listen on listen socket"); + goto error; + } + + if (mStatus_NoError != udsSupportAddFDToEventLoop(listenfd, connect_callback, (void *) NULL)) + { + my_perror("ERROR: could not add listen socket to event loop"); + goto error; + } + +#if !defined(PLATFORM_NO_RLIMIT) + { + // Set maximum number of open file descriptors + #define MIN_OPENFILES 10240 + struct rlimit maxfds, newfds; + + // Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>) + // you have to get and set rlimits once before getrlimit will return sensible values + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); + + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES; + newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES; + if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur) + if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); + + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max); + debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur); + } +#endif + + return 0; + +error: + + my_perror("ERROR: udsserver_init"); + return -1; + } + +int udsserver_exit(void) + { + dnssd_close(listenfd); + +#if !defined(USE_TCP_LOOPBACK) + // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody" + // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket. + // It would be nice if we could find a solution to this problem + if (unlink(MDNS_UDS_SERVERPATH)) + debugf("Unable to remove %s", MDNS_UDS_SERVERPATH); +#endif + + if (PID_FILE[0]) unlink(PID_FILE); + + return 0; + } + +mDNSs32 udsserver_idle(mDNSs32 nextevent) + { + request_state *req = all_requests, *tmp, *prev = NULL; + reply_state *fptr; + transfer_state result; + mDNSs32 now = mDNS_TimeNow(&mDNSStorage); + + while(req) + { + result = t_uninitialized; + if (req->u_err) + result = send_undelivered_error(req); + if (result != t_error && result != t_morecoming && // don't try to send msg if send_error failed + (req->ts == t_complete || req->ts == t_morecoming)) + { + while(req->replies) + { + if (req->replies->next) req->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing); + result = send_msg(req->replies); + if (result == t_complete) + { + fptr = req->replies; + req->replies = req->replies->next; + freeL("udsserver_idle", fptr); + req->time_blocked = 0; // reset failure counter after successful send + } + else if (result == t_terminated || result == t_error) + { + abort_request(req); + break; + } + else if (result == t_morecoming) break; // client's queues are full, move to next + } + } + if (result == t_morecoming) + { + if (!req->time_blocked) req->time_blocked = now; + debugf("udsserver_idle: client has been blocked for %ld seconds", (now - req->time_blocked) / mDNSPlatformOneSecond); + if (now - req->time_blocked >= MAX_TIME_BLOCKED) + { + LogMsg("Could not write data to client %d after %ld seconds - aborting connection", req->sd, MAX_TIME_BLOCKED / mDNSPlatformOneSecond); + LogClientInfo(req); + abort_request(req); + result = t_terminated; + } + else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; // try again in a second + } + if (result == t_terminated || result == t_error) + //since we're already doing a list traversal, we unlink the request manually instead of calling unlink_request() + { + tmp = req; + if (prev) prev->next = req->next; + if (req == all_requests) all_requests = all_requests->next; + req = req->next; + freeL("udsserver_idle", tmp); + } + else + { + prev = req; + req = req->next; + } + } + return nextevent; + } + +mDNSexport void udsserver_info(mDNS *const m) + { + mDNSs32 now = mDNS_TimeNow(m); + mDNSu32 CacheUsed = 0, CacheActive = 0; + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + request_state *req; + + LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now); + + LogMsgNoIdent("Slt Q TTL U Type if len rdata"); + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for(cg = m->rrcache_hash[slot]; cg; cg=cg->next) + { + CacheUsed++; // Count one cache entity for the CacheGroup object + for (rr = cg->members; rr; rr=rr->next) + { + mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond; + CacheUsed++; + if (rr->CRActiveQuestion) CacheActive++; + LogMsgNoIdent("%3d %s%6ld %s %-6s%-6s%s", + slot, + rr->CRActiveQuestion ? "*" : " ", + remain, + (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "-" : " ", + DNSTypeName(rr->resrec.rrtype), + ((NetworkInterfaceInfo *)rr->resrec.InterfaceID)->ifname, + CRDisplayString(m, rr)); + usleep(1000); // Limit rate a little so we don't flood syslog too fast + } + } + + if (m->rrcache_totalused != CacheUsed) + LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); + if (m->rrcache_active != CacheActive) + LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive); + LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive); + + for (req = all_requests; req; req=req->next) + LogClientInfo(req); + + now = mDNS_TimeNow(m); + LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now); + } + +#if __MACOSX__ && MACOSX_MDNS_MALLOC_DEBUGGING +mDNSexport void uds_validatelists(void) + { + request_state *req; + for (req = all_requests; req; req=req->next) + if (req->sd < 0 && req->sd != -2) + LogMemCorruption("UDS request list: %p is garbage (%X)", req, req->sd); + } +#endif + +mDNSlocal void rename_service(service_instance *srv) + { + if (srv->autoname && !SameDomainLabel(srv->name.c, gmDNS->nicelabel.c)) + { + srv->rename_on_memfree = 1; + if (mDNS_DeregisterService(gmDNS, &srv->srs)) // If service deregistered already, we can re-register immediately + regservice_callback(gmDNS, &srv->srs, mStatus_MemFree); + } + } + +mDNSexport void udsserver_handle_configchange(void) + { + request_state *req; + + for (req = all_requests; req; req = req->next) + { + if (req->service_registration) + { + service_instance *ptr; + for (ptr = req->service_registration->instances; ptr; ptr = ptr->next) + rename_service(ptr); + } + } + } + +mDNSlocal void connect_callback(void *info) + { + dnssd_sock_t sd; + dnssd_socklen_t len; + unsigned long optval; + dnssd_sockaddr_t cliaddr; + request_state *rstate; + (void)info; // Unused + + len = (dnssd_socklen_t) sizeof(cliaddr); + + sd = accept(listenfd, (struct sockaddr*) &cliaddr, &len); + + if (sd == dnssd_InvalidSocket) + { + if (dnssd_errno() == dnssd_EWOULDBLOCK) return; + my_perror("ERROR: accept"); + return; + } + optval = 1; + +#ifdef SO_NOSIGPIPE + // Some environments (e.g. OS X) support turning off SIGPIPE for a socket + if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) + { + my_perror("ERROR: setsockopt - SO_NOSIGPIPE - aborting client"); + dnssd_close(sd); + return; + } +#endif + +#if defined(_WIN32) + if (ioctlsocket(sd, FIONBIO, &optval) != 0) +#else + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) +#endif + { + my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client"); + dnssd_close(sd); + return; + } + + // allocate a request_state struct that will live with the socket + rstate = mallocL("connect_callback", sizeof(request_state)); + if (!rstate) FatalError("ERROR: malloc"); + bzero(rstate, sizeof(request_state)); + rstate->ts = t_morecoming; + rstate->sd = sd; + + LogOperation("%3d: Adding FD", rstate->sd); + if ( mStatus_NoError != udsSupportAddFDToEventLoop( sd, request_callback, rstate)) + return; + rstate->next = all_requests; + all_requests = rstate; + } + +// handler +mDNSlocal void request_callback(void *info) + { + request_state *rstate = info; + transfer_state result; + dnssd_sockaddr_t cliaddr; + int dedicated_error_socket; +#if defined(_WIN32) + u_long opt = 1; +#endif + + result = read_msg(rstate); + if (result == t_morecoming) + { + return; + } + if (result == t_terminated) + { + abort_request(rstate); + unlink_request(rstate); + return; + } + if (result == t_error) + { + abort_request(rstate); + unlink_request(rstate); + return; + } + + if (rstate->hdr.version != VERSION) + { + LogMsg("ERROR: client incompatible with daemon (client version = %d, " + "daemon version = %d)\n", rstate->hdr.version, VERSION); + abort_request(rstate); + unlink_request(rstate); + return; + } + + if (validate_message(rstate) < 0) + { + // note that we cannot deliver an error message if validation fails, since the path to the error socket + // may be contained in the (invalid) message body for some message types + abort_request(rstate); + unlink_request(rstate); + LogMsg("Invalid message sent by client - may indicate a malicious program running on this machine!"); + return; + } + + // check if client wants silent operation + if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1; + + dedicated_error_socket = (rstate->hdr.op == reg_record_request || rstate->hdr.op == add_record_request || + rstate->hdr.op == update_record_request || rstate->hdr.op == remove_record_request); + + if (((rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET) == 0) != dedicated_error_socket) + LogMsg("WARNING: client request %d with incorrect flags setting 0x%X", rstate->hdr.op, rstate->hdr.flags); + + // check if primary socket is to be used for synchronous errors, else open new socket + if (dedicated_error_socket) + { + mStatus err = 0; + int nwritten; + dnssd_sock_t errfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (errfd == dnssd_InvalidSocket) + { + my_perror("ERROR: socket"); + abort_request(rstate); + unlink_request(rstate); + return; + } + + //LogOperation("request_callback: Opened dedicated errfd %d", errfd); + + #if defined(USE_TCP_LOOPBACK) + { + mDNSOpaque16 port; + port.b[0] = rstate->msgdata[0]; + port.b[1] = rstate->msgdata[1]; + rstate->msgdata += 2; + cliaddr.sin_family = AF_INET; + cliaddr.sin_port = port.NotAnInteger; + cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + } + #else + { + char ctrl_path[MAX_CTLPATH]; + get_string(&rstate->msgdata, ctrl_path, 256); // path is first element in message buffer + bzero(&cliaddr, sizeof(cliaddr)); + cliaddr.sun_family = AF_LOCAL; + strcpy(cliaddr.sun_path, ctrl_path); + } + #endif + //LogOperation("request_callback: Connecting to “%s”", cliaddr.sun_path); + if (connect(errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) + { + //LogOperation("request_callback: Couldn't connect to “%s”", cliaddr.sun_path); + my_perror("ERROR: connect"); + abort_request(rstate); + unlink_request(rstate); + return; + } +#if defined(_WIN32) + if (ioctlsocket(errfd, FIONBIO, &opt) != 0) +#else + if (fcntl(errfd, F_SETFL, O_NONBLOCK) != 0) +#endif + { + my_perror("ERROR: could not set control socket to non-blocking mode"); + abort_request(rstate); + unlink_request(rstate); + return; + } + + switch(rstate->hdr.op) + { + case reg_record_request: err = handle_regrecord_request (rstate); break; + case add_record_request: err = handle_add_request (rstate); break; + case update_record_request: err = handle_update_request (rstate); break; + case remove_record_request: err = handle_removerecord_request(rstate); break; + default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op); + } + + //LogOperation("request_callback: Returning error code %d on socket %d", err, errfd); + err = dnssd_htonl(err); + nwritten = send(errfd, (dnssd_sockbuf_t) &err, sizeof(err), 0); + // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a four-byte write for us. + // If not, we don't attempt to handle this failure, but we do log it. + if (nwritten < (int)sizeof(err)) + LogMsg("ERROR: failed to write error response back to caller: %d %d %s", + nwritten, dnssd_errno(), dnssd_strerror(dnssd_errno())); + //else LogOperation("request_callback: Returned error code %d on socket %d", err, errfd); + dnssd_close(errfd); + //LogOperation("request_callback: Closed errfd %d", errfd); + reset_connected_rstate(rstate); // Reset ready to accept the next request on this pipe + } + else + { + switch(rstate->hdr.op) + { + case resolve_request: handle_resolve_request (rstate); break; + case query_request: handle_query_request (rstate); break; + case browse_request: handle_browse_request (rstate); break; + case reg_service_request: handle_regservice_request(rstate); break; + case enumeration_request: handle_enum_request (rstate); break; + case reconfirm_record_request: handle_reconfirm_request (rstate); break; + case setdomain_request: handle_setdomain_request (rstate); break; + default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op); + } + } + } + +// mDNS operation functions. Each operation has 3 associated functions - a request handler that parses +// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback +// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts +// the mDNSCore operation if the client dies or closes its socket. + +// query and resolve calls have separate request handlers that parse the arguments from the client and +// massage the name parameters appropriately, but the rest of the operations (making the query call, +// delivering the result to the client, and termination) are identical. + +mDNSlocal void handle_query_request(request_state *rstate) + { + DNSServiceFlags flags; + uint32_t ifi; + char name[256]; + uint16_t rrtype, rrclass; + char *ptr; + mStatus result; + mDNSInterfaceID InterfaceID; + DNSQuestion *q; + + if (rstate->ts != t_complete) + { + LogMsg("ERROR: handle_query_request - transfer state != t_complete"); + goto error; + } + ptr = rstate->msgdata; + if (!ptr) + { + LogMsg("ERROR: handle_query_request - NULL msgdata"); + goto error; + } + + flags = get_flags(&ptr); + ifi = get_long(&ptr); + if (get_string(&ptr, name, 256) < 0) goto bad_param; + rrtype = get_short(&ptr); + rrclass = get_short(&ptr); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi); + if (ifi && !InterfaceID) goto bad_param; + + q = mallocL("DNSQuestion", sizeof(DNSQuestion)); + if (!q) FatalError("ERROR: handle_query - malloc"); + bzero(q, sizeof(DNSQuestion)); + + q->InterfaceID = InterfaceID; + q->Target = zeroAddr; + if (!MakeDomainNameFromDNSNameString(&q->qname, name)) { freeL("DNSQuestion", q); goto bad_param; } + q->qtype = rrtype; + q->qclass = rrclass; + q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery) != 0; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; + q->ReturnCNAME = (flags & kDNSServiceFlagsReturnCNAME) != 0; + q->QuestionCallback = question_result_callback; + q->QuestionContext = rstate; + + rstate->termination_context = q; + rstate->terminate = question_termination_callback; + + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) START", rstate->sd, q->qname.c, DNSTypeName(q->qtype)); + result = mDNS_StartQuery(gmDNS, q); + if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result); + + if (result) rstate->terminate = NULL; + if (deliver_error(rstate, result) < 0) goto error; + return; + +bad_param: + deliver_error(rstate, mStatus_BadParamErr); + rstate->terminate = NULL; // don't try to terminate insuccessful Core calls +error: + abort_request(rstate); + unlink_request(rstate); + return; + } + +mDNSlocal void handle_resolve_request(request_state *rstate) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + mDNSInterfaceID InterfaceID; + char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; + char *ptr; // message data pointer + domainname fqdn; + resolve_termination_t *term; + mStatus err; + + if (rstate->ts != t_complete) + { + LogMsg("ERROR: handle_resolve_request - transfer state != t_complete"); + abort_request(rstate); + unlink_request(rstate); + return; + } + + // extract the data from the message + ptr = rstate->msgdata; + if (!ptr) + { + LogMsg("ERROR: handle_resolve_request - NULL msgdata"); + abort_request(rstate); + unlink_request(rstate); + return; + } + flags = get_flags(&ptr); + interfaceIndex = get_long(&ptr); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex); + if (interfaceIndex && !InterfaceID) + { LogMsg("ERROR: handle_resolve_request - Couldn't find InterfaceID for interfaceIndex %d", interfaceIndex); goto bad_param; } + if (get_string(&ptr, name, 256) < 0 || + get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) + { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); goto bad_param; } + + // free memory in rstate since we don't need it anymore + freeL("handle_resolve_request", rstate->msgbuf); + rstate->msgbuf = NULL; + + if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0) + { LogMsg("ERROR: handle_resolve_request - Couldn't build_domainname_from_strings “%s” “%s” “%s”", name, regtype, domain); goto bad_param; } + + // set up termination info + term = mallocL("handle_resolve_request", sizeof(resolve_termination_t)); + bzero(term, sizeof(*term)); + if (!term) FatalError("ERROR: malloc"); + + // format questions + term->qsrv.InterfaceID = InterfaceID; + term->qsrv.Target = zeroAddr; + memcpy(&term->qsrv.qname, &fqdn, MAX_DOMAIN_NAME); + term->qsrv.qtype = kDNSType_SRV; + term->qsrv.qclass = kDNSClass_IN; + term->qsrv.LongLived = mDNSfalse; + term->qsrv.ExpectUnique = mDNStrue; + term->qsrv.ForceMCast = mDNSfalse; + term->qsrv.QuestionCallback = resolve_result_callback; + term->qsrv.QuestionContext = rstate; + + term->qtxt.InterfaceID = InterfaceID; + term->qtxt.Target = zeroAddr; + memcpy(&term->qtxt.qname, &fqdn, MAX_DOMAIN_NAME); + term->qtxt.qtype = kDNSType_TXT; + term->qtxt.qclass = kDNSClass_IN; + term->qtxt.LongLived = mDNSfalse; + term->qtxt.ExpectUnique = mDNStrue; + term->qtxt.ForceMCast = mDNSfalse; + term->qtxt.QuestionCallback = resolve_result_callback; + term->qtxt.QuestionContext = rstate; + + term->rstate = rstate; + rstate->termination_context = term; + rstate->terminate = resolve_termination_callback; + + // ask the questions + LogOperation("%3d: DNSServiceResolve(%##s) START", rstate->sd, term->qsrv.qname.c); + err = mDNS_StartQuery(gmDNS, &term->qsrv); + if (!err) err = mDNS_StartQuery(gmDNS, &term->qtxt); + + if (err) + { + freeL("handle_resolve_request", term); + rstate->terminate = NULL; // prevent abort_request() from invoking termination callback + } + if (deliver_error(rstate, err) < 0 || err) + { + abort_request(rstate); + unlink_request(rstate); + } + return; + +bad_param: + deliver_error(rstate, mStatus_BadParamErr); + abort_request(rstate); + unlink_request(rstate); + } + +mDNSlocal void resolve_termination_callback(void *context) + { + resolve_termination_t *term = context; + request_state *rs; + + if (!term) + { + LogMsg("ERROR: resolve_termination_callback: double termination"); + return; + } + rs = term->rstate; + LogOperation("%3d: DNSServiceResolve(%##s) STOP", rs->sd, term->qtxt.qname.c); + + mDNS_StopQuery(gmDNS, &term->qtxt); + mDNS_StopQuery(gmDNS, &term->qsrv); + + freeL("resolve_termination_callback", term); + rs->termination_context = NULL; + } + +mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + size_t len = 0; + char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME]; + char *data; + transfer_state result; + reply_state *rep; + request_state *rs = question->QuestionContext; + resolve_termination_t *res = rs->termination_context; + (void)m; // Unused + + LogOperation("%3d: DNSServiceResolve(%##s, %s) %s %s", + rs->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); + + // This code used to do this trick of just keeping a copy of the pointer to + // the answer record in the cache, but the unicast query code doesn't currently + // put its answer records in the cache, so for now we can't do this. + + if (!AddRecord) + { + // After unicast query code is updated to store its records in the common cache, use this... + // if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL; + // if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL; + // intead of this... + if (answer->rrtype == kDNSType_SRV && res->srv && SameRDataBody(answer, (RDataBody *)&res->srvdata)) + res->srv = mDNSfalse; + if (answer->rrtype == kDNSType_TXT && res->txt && answer->rdlength == res->txtlen && SameRDataBody(answer, (RDataBody *)&res->txtdata)) + res->txt = mDNSfalse; + return; + } + + // After unicast query code is updated to store its records in the common cache, use this... + // if (answer->rrtype == kDNSType_SRV) res->srv = answer; + // if (answer->rrtype == kDNSType_TXT) res->txt = answer; + // intead of this... + if (answer->rrtype == kDNSType_SRV) + { + res->srvdata = answer->rdata->u.srv; + res->srv = mDNStrue; + } + if (answer->rrtype == kDNSType_TXT) + { + if (answer->rdlength > AbsoluteMaxDNSMessageData) return; + res->txtlen = answer->rdlength; + mDNSPlatformMemCopy(answer->rdata->u.data, res->txtdata, res->txtlen); + res->txt = mDNStrue; + } + + if (!res->txt || !res->srv) return; // only deliver result to client if we have both answers + + ConvertDomainNameToCString(answer->name, fullname); + ConvertDomainNameToCString(&res->srvdata.target, target); + + // calculate reply length + len += sizeof(DNSServiceFlags); + len += sizeof(uint32_t); // interface index + len += sizeof(DNSServiceErrorType); + len += strlen(fullname) + 1; + len += strlen(target) + 1; + len += 2 * sizeof(uint16_t); // port, txtLen + len += res->txtlen; + + // allocate/init reply header + rep = create_reply(resolve_reply_op, len, rs); + rep->rhdr->flags = dnssd_htonl(0); + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID)); + rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); + + data = rep->sdata; + + // write reply data to message + put_string(fullname, &data); + put_string(target, &data); + *data++ = res->srvdata.port.b[0]; + *data++ = res->srvdata.port.b[1]; + put_short(res->txtlen, &data); + put_rdata(res->txtlen, res->txtdata, &data); + + result = send_msg(rep); + if (result == t_error || result == t_terminated) + { + abort_request(rs); + unlink_request(rs); + freeL("resolve_result_callback", rep); + } + else if (result == t_complete) freeL("resolve_result_callback", rep); + else append_reply(rs, rep); + } + +// what gets called when a resolve is completed and we need to send the data back to the client +mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + char *data; + char name[MAX_ESCAPED_DOMAIN_NAME]; + request_state *req = question->QuestionContext; + reply_state *rep; + size_t len; + (void)m; // Unused + + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) RESULT %s", req->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer)); + //mDNS_StopQuery(m, question); + + if (answer->rdlength == 0) + { + deliver_async_error(req, query_reply_op, kDNSServiceErr_NoSuchRecord); + return; + } + + // calculate reply data length + len = sizeof(DNSServiceFlags); + len += 2 * sizeof(uint32_t); // if index + ttl + len += sizeof(DNSServiceErrorType); + len += 3 * sizeof(uint16_t); // type, class, rdlen + len += answer->rdlength; + ConvertDomainNameToCString(answer->name, name); + len += strlen(name) + 1; + + rep = create_reply(query_reply_op, len, req); + + rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0); + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID)); + rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); + + data = rep->sdata; + + put_string(name, &data); + put_short(answer->rrtype, &data); + put_short(answer->rrclass, &data); + put_short(answer->rdlength, &data); + put_rdata(answer->rdlength, answer->rdata->u.data, &data); + put_long(AddRecord ? answer->rroriginalttl : 0, &data); + + append_reply(req, rep); + return; + } + +mDNSlocal void question_termination_callback(void *context) + { + DNSQuestion *q = context; + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", ((request_state *)q->QuestionContext)->sd, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StopQuery(gmDNS, q); // no need to error check + freeL("question_termination_callback", q); + } + +// If there's a comma followed by another character, +// FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character. +// Otherwise, it returns a pointer to the final nul at the end of the string +mDNSlocal char *FindFirstSubType(char *p) + { + while (*p) + { + if (p[0] == '\\' && p[1]) p += 2; + else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); } + else p++; + } + return(p); + } + +// If there's a comma followed by another character, +// FindNextSubType overwrites the comma with a nul and returns the pointer to the next character. +// If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL +// Otherwise, it returns a pointer to the final nul at the end of the string +mDNSlocal char *FindNextSubType(char *p) + { + while (*p) + { + if (p[0] == '\\' && p[1]) // If escape character + p += 2; // ignore following character + else if (p[0] == ',') // If we found a comma + { + if (p[1]) *p++ = 0; + return(p); + } + else if (p[0] == '.') + return(mDNSNULL); + else p++; + } + return(p); + } + +// Returns -1 if illegal subtype found +mDNSexport mDNSs32 ChopSubTypes(char *regtype) + { + mDNSs32 NumSubTypes = 0; + char *stp = FindFirstSubType(regtype); + while (stp && *stp) // If we found a comma... + { + if (*stp == ',') return(-1); + NumSubTypes++; + stp = FindNextSubType(stp); + } + if (!stp) return(-1); + return(NumSubTypes); + } + +mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) + { + AuthRecord *st = mDNSNULL; + if (NumSubTypes) + { + mDNSs32 i; + st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); + if (!st) return(mDNSNULL); + for (i = 0; i < NumSubTypes; i++) + { + mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL); + while (*p) p++; + p++; + if (!MakeDomainNameFromDNSNameString(st[i].resrec.name, p)) + { freeL("ServiceSubTypes", st); return(mDNSNULL); } + } + } + return(st); + } + +#ifdef _HAVE_SETDOMAIN_SUPPORT_ +mDNSlocal void free_defdomain(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)m; // unused + if (result == mStatus_MemFree) free(rr->RecordContext); // context is the enclosing list structure + } +#endif + +mDNSlocal void handle_setdomain_request(request_state *request) + { + mStatus err = mStatus_NoError; + char *ptr; + char domainstr[MAX_ESCAPED_DOMAIN_NAME]; + domainname domain; + DNSServiceFlags flags; +#ifdef _HAVE_SETDOMAIN_SUPPORT_ + struct xucred xuc; + socklen_t xuclen; +#endif + + if (request->ts != t_complete) + { + LogMsg("ERROR: handle_setdomain_request - transfer state != t_complete"); + abort_request(request); + unlink_request(request); + return; + } + + // extract flags/domain from message + ptr = request->msgdata; + flags = get_flags(&ptr); + if (get_string(&ptr, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 || + !MakeDomainNameFromDNSNameString(&domain, domainstr)) + { err = mStatus_BadParamErr; goto end; } + + freeL("handle_setdomain_request", request->msgbuf); + request->msgbuf = NULL; + + debugf("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c); + +#ifdef _HAVE_SETDOMAIN_SUPPORT_ + // this functionality currently only used for Apple-specific configuration, so we don't burned other platforms by mandating + // the existence of this socket option + xuclen = sizeof(xuc); + if (getsockopt(request->sd, 0, LOCAL_PEERCRED, &xuc, &xuclen)) + { my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); err = mStatus_UnknownErr; goto end; } + if (xuc.cr_version != XUCRED_VERSION) { LogMsg("getsockopt, LOCAL_PEERCRED - bad version"); err = mStatus_UnknownErr; goto end; } + LogMsg("Default domain %s %s for UID %d", domainstr, flags & kDNSServiceFlagsAdd ? "set" : "removed", xuc.cr_uid); + + if (flags & kDNSServiceFlagsAdd) + { + // register a local-only PRT record + default_browse_list_t *newelem = malloc(sizeof(default_browse_list_t)); + if (!newelem) { LogMsg("ERROR: malloc"); err = mStatus_NoMemoryErr; goto end; } + mDNS_SetupResourceRecord(&newelem->ptr_rec, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, free_defdomain, newelem); + MakeDomainNameFromDNSNameString(&newelem->ptr_rec.resrec.name, mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]); + AppendDNSNameString (&newelem->ptr_rec.resrec.name, "local"); + AssignDomainName(&newelem->ptr_rec.resrec.rdata->u.name, &domain); + newelem->uid = xuc.cr_uid; + err = mDNS_Register(gmDNS, &newelem->ptr_rec); + if (err) free(newelem); + else + { + // link into list + newelem->next = default_browse_list; + default_browse_list = newelem; + } + + } + else + { + // remove - find in list, deregister + default_browse_list_t *ptr = default_browse_list, *prev = NULL; + while (ptr) + { + if (SameDomainName(&ptr->ptr_rec.resrec.rdata->u.name, &domain)) + { + if (prev) prev->next = ptr->next; + else default_browse_list = ptr->next; + err = mDNS_Deregister(gmDNS, &ptr->ptr_rec); + break; + } + prev = ptr; + ptr = ptr->next; + } + if (!ptr) { LogMsg("Attempt to remove nonexistent domain %s for UID %d", domainstr, xuc.cr_uid); err = mStatus_Invalid; } + } +#else + err = mStatus_NoError; +#endif // _HAVE_SETDOMAIN_SUPPORT_ + + end: + deliver_error(request, err); + abort_request(request); + unlink_request(request); + } + +// Generates a response message giving name, type, domain, plus interface index, +// suitable for a browse result or service registration result. +// On successful completion rep is set to point to a malloc'd reply_state struct +mDNSlocal mStatus GenerateNTDResponse(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep) + { + domainlabel name; + domainname type, dom; + *rep = NULL; + if (!DeconstructServiceName(servicename, &name, &type, &dom)) + return kDNSServiceErr_Invalid; + else + { + char namestr[MAX_DOMAIN_LABEL+1]; + char typestr[MAX_ESCAPED_DOMAIN_NAME]; + char domstr [MAX_ESCAPED_DOMAIN_NAME]; + int len; + char *data; + + ConvertDomainLabelToCString_unescaped(&name, namestr); + ConvertDomainNameToCString(&type, typestr); + ConvertDomainNameToCString(&dom, domstr); + + // Calculate reply data length + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); // if index + len += sizeof(DNSServiceErrorType); + len += (int) (strlen(namestr) + 1); + len += (int) (strlen(typestr) + 1); + len += (int) (strlen(domstr) + 1); + + // Build reply header + *rep = create_reply(query_reply_op, len, request); + (*rep)->rhdr->flags = dnssd_htonl(0); + (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id)); + (*rep)->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); + + // Build reply body + data = (*rep)->sdata; + put_string(namestr, &data); + put_string(typestr, &data); + put_string(domstr, &data); + + return mStatus_NoError; + } + } + +mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + request_state *req = question->QuestionContext; + reply_state *rep; + (void)m; // Unused + + if (answer->rrtype != kDNSType_PTR) + { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; } + + if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep) != mStatus_NoError) + { + LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer", + req->sd, answer->name->c, answer->rdata->u.name.c); + return; + } + + LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %s", + req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer)); + + if (AddRecord) rep->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsAdd); + append_reply(req, rep); + } + +mDNSlocal mStatus add_domain_to_browser(browser_info_t *info, const domainname *d) + { + browser_t *b, *p; + mStatus err; + + for (p = info->browsers; p; p = p->next) + { + if (SameDomainName(&p->domain, d)) + { debugf("add_domain_to_browser - attempt to add domain %##d already in list", d->c); return mStatus_AlreadyRegistered; } + } + + b = mallocL("browser_t", sizeof(*b)); + if (!b) return mStatus_NoMemoryErr; + AssignDomainName(&b->domain, d); + err = mDNS_StartBrowse(gmDNS, &b->q, &info->regtype, d, info->interface_id, info->ForceMCast, FoundInstance, info->rstate); + if (err) + { + LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->regtype.c, d->c); + freeL("browser_t", b); + } + else + { + b->next = info->browsers; + info->browsers = b; + } + return err; + } + +mDNSlocal void handle_browse_request(request_state *request) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + mDNSInterfaceID InterfaceID; + char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; + domainname typedn, d, temp; + mDNSs32 NumSubTypes; + char *ptr; + mStatus err = mStatus_NoError; + DNameListElem *search_domain_list, *sdom; + browser_info_t *info = NULL; + + if (request->ts != t_complete) + { + LogMsg("ERROR: handle_browse_request - transfer state != t_complete"); + abort_request(request); + unlink_request(request); + return; + } + + // extract data from message + ptr = request->msgdata; + flags = get_flags(&ptr); + interfaceIndex = get_long(&ptr); + if (get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) + { err = mStatus_BadParamErr; goto error; } + freeL("handle_browse_request", request->msgbuf); + request->msgbuf = NULL; + + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex); + if (interfaceIndex && !InterfaceID) { err = mStatus_BadParamErr; goto error; } + +#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS) + if ( !domain || ( domain[0] == '\0' ) ) + { + dDNS_RegisterSearchDomains( gmDNS ); + } +#endif + + typedn.c[0] = 0; + NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0 || NumSubTypes > 1) { err = mStatus_BadParamErr; goto error; } + if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) + { err = mStatus_BadParamErr; goto error; } + + if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) { err = mStatus_BadParamErr; goto error; } + + if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { err = mStatus_BadParamErr; goto error; } + if (temp.c[0] > 15 && domain[0] == 0) strcpy(domain, "local."); // For over-long service types, we only allow domain "local" + + // allocate and set up browser info + info = mallocL("browser_info_t", sizeof(*info)); + if (!info) { err = mStatus_NoMemoryErr; goto error; } + + request->browser_info = info; + info->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; + info->interface_id = InterfaceID; + AssignDomainName(&info->regtype, &typedn); + info->rstate = request; + info->default_domain = !domain[0]; + info->browsers = NULL; + + // setup termination context + request->termination_context = info; + request->terminate = browse_termination_callback; + + LogOperation("%3d: DNSServiceBrowse(\"%##s\", \"%s\") START", request->sd, info->regtype.c, domain); + if (domain[0]) + { + if (!MakeDomainNameFromDNSNameString(&d, domain)) { err = mStatus_BadParamErr; goto error; } + err = add_domain_to_browser(info, &d); + } + + else + { + search_domain_list = mDNSPlatformGetSearchDomainList(); + for (sdom = search_domain_list; sdom; sdom = sdom->next) + { + err = add_domain_to_browser(info, &sdom->name); + if (err) + { + if (SameDomainName(&sdom->name, &localdomain)) break; + else err = mStatus_NoError; // suppress errors for non-local "default" domains + } + + } + mDNS_FreeDNameList(search_domain_list); + } + + deliver_error(request, err); + return; + +error: + if (info) freeL("browser_info_t", info); + if (request->termination_context) request->termination_context = NULL; + deliver_error(request, err); + abort_request(request); + unlink_request(request); + } + +mDNSlocal void browse_termination_callback(void *context) + { + browser_info_t *info = context; + browser_t *ptr; + + if (!info) return; + + while(info->browsers) + { + ptr = info->browsers; + info->browsers = ptr->next; + LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->rstate->sd, ptr->q.qname.c); + mDNS_StopBrowse(gmDNS, &ptr->q); // no need to error-check result + freeL("browse_termination_callback", ptr); + } + + info->rstate->termination_context = NULL; + freeL("browser_info", info); + } + +mDNSexport void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add) + { + request_state *r; + + for (r = all_requests; r; r = r->next) + { + browser_info_t *info = r->browser_info; + + if (!info || !info->default_domain) continue; + if (add) add_domain_to_browser(info, d); + else + { + browser_t **ptr = &info->browsers; + while (*ptr) + { + if (SameDomainName(&(*ptr)->domain, d)) + { + browser_t *remove = *ptr; + *ptr = (*ptr)->next; + if (remove->q.LongLived) + { + // Give goodbyes for known answers. + // Note that this a special case where we know that the QuestionCallback function is our own + // code (it's FoundInstance), and that callback routine doesn't ever cancel its operation, so we + // don't need to guard against the question being cancelled mid-loop the way the mDNSCore routines do. + CacheRecord *ka = remove->q.uDNS_info.knownAnswers; + while (ka) { remove->q.QuestionCallback(gmDNS, &remove->q, &ka->resrec, mDNSfalse); ka = ka->next; } + } + mDNS_StopBrowse(gmDNS, &remove->q); + freeL("browser_t", remove); + return; + } + ptr = &(*ptr)->next; + } + LogMsg("Requested removal of default domain %##s not in list for sd %d", d->c, r->sd); + } + } + } + +// Count how many other service records we have locally with the same name, but different rdata. +// For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of +// the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming. +mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs) + { + int count = 0; + ResourceRecord *r = &srs->RR_SRV.resrec; + AuthRecord *rr; + ServiceRecordSet *s; + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r)) + count++; + + for (rr = m->uDNS_info.RecordRegistrations; rr; rr=rr->next) + if (rr->uDNS_info.state != regState_Unregistered && rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r)) + count++; + + for (s = m->uDNS_info.ServiceRegistrations; s; s = s->next) + if (s->uDNS_info.state != regState_Unregistered && SameDomainName(s->RR_SRV.resrec.name, r->name) && !SameRData(&s->RR_SRV.resrec, r)) + count++; + + verbosedebugf("%d peer registrations for %##s", count, r->name->c); + return(count); + } + +mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port) + { + int count = 0; + AuthRecord *rr; + for (rr = gmDNS->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && + rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger && + SameDomainName(rr->resrec.name, srv)) + count++; + return(count); + } + +mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain) + { + service_info *info = request->service_registration; + service_instance *ptr, *instance; + int instance_size; + mStatus result; + + for (ptr = info->instances; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->domain, domain)) + { LogMsg("register_service_instance: domain %##s already registered", domain->c); return mStatus_AlreadyRegistered; } + } + + instance_size = sizeof(*instance); + if (info->txtlen > sizeof(RDataBody)) instance_size += (info->txtlen - sizeof(RDataBody)); + instance = mallocL("service_instance", instance_size); + if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } + + instance->subtypes = AllocateSubTypes(info->num_subtypes, info->type_as_string); + if (info->num_subtypes && !instance->subtypes) + { free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); } + instance->request = request; + instance->sd = request->sd; + instance->autoname = info->autoname; + instance->autorename = info->autorename; + instance->allowremotequery = info->allowremotequery; + instance->rename_on_memfree = 0; + instance->name = info->name; + AssignDomainName(&instance->domain, domain); + instance->default_local = (info->default_domain && SameDomainName(domain, &localdomain)); + result = mDNS_RegisterService(gmDNS, &instance->srs, &instance->name, &info->type, domain, info->host.c[0] ? &info->host : NULL, info->port, + info->txtdata, info->txtlen, instance->subtypes, info->num_subtypes, info->InterfaceID, regservice_callback, instance); + + if (result) free_service_instance(instance); + else + { + instance->next = info->instances; + info->instances = instance; + } + return result; + } + +mDNSexport void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add) + { + request_state *rstate; + service_info *info; + + LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->c); + for (rstate = all_requests; rstate; rstate = rstate->next) + { + if (rstate->terminate != regservice_termination_callback) continue; + info = rstate->service_registration; + if (!info) { LogMsg("udsserver_default_reg_domain_changed - NULL service info"); continue; } // this should never happen + if (!info->default_domain) continue; + + // valid default registration + if (add) register_service_instance(rstate, d); + else + { + // find the instance to remove + service_instance *si = rstate->service_registration->instances, *prev = NULL; + while (si) + { + if (SameDomainName(&si->domain, d)) + { + mStatus err; + if (prev) prev->next = si->next; + else info->instances = si->next; + err = mDNS_DeregisterService(gmDNS, &si->srs); + if (err) + { + LogMsg("udsserver_default_reg_domain_changed - mDNS_DeregisterService err %d", err); + free_service_instance(si); + } + break; + } + prev = si; + si = si->next; + } + if (!si) debugf("udsserver_default_reg_domain_changed - domain %##s not registered", d->c); // normal if registration failed + } + } + } + +// service registration +mDNSlocal void handle_regservice_request(request_state *request) + { + DNSServiceFlags flags; + uint32_t ifi; + char name[1024]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes + char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME]; + char *ptr; + domainname d, srv; + mStatus result; + service_info *service = NULL; + + if (request->ts != t_complete) + { + LogMsg("ERROR: handle_regservice_request - transfer state != t_complete"); + abort_request(request); + unlink_request(request); + return; + } + + service = mallocL("service_info", sizeof(*service)); + if (!service) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; } + + service->instances = NULL; + service->request = request; + service->txtlen = 0; + service->txtdata = NULL; + request->service_registration = service; + request->termination_context = request->service_registration; + request->terminate = regservice_termination_callback; + + // extract data from message + ptr = request->msgdata; + flags = get_flags(&ptr); + ifi = get_long(&ptr); + service->InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi); + if (ifi && !service->InterfaceID) + { LogMsg("ERROR: handle_regservice_request - Couldn't find InterfaceID for interfaceIndex %d", ifi); goto bad_param; } + if (get_string(&ptr, name, sizeof(name)) < 0 || + get_string(&ptr, service->type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&ptr, host, MAX_ESCAPED_DOMAIN_NAME) < 0) + { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); goto bad_param; } + + service->port.b[0] = *ptr++; + service->port.b[1] = *ptr++; + + service->txtlen = get_short(&ptr); + if (service->txtlen) + { + service->txtdata = mallocL("txtdata", service->txtlen); + if (!service->txtdata) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; } + memcpy(service->txtdata, get_rdata(&ptr, service->txtlen), service->txtlen); + } + else service->txtdata = NULL; + + // Check for sub-types after the service type + service->num_subtypes = ChopSubTypes(service->type_as_string); // Note: Modifies regtype string to remove trailing subtypes + if (service->num_subtypes < 0) + { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", service->type_as_string); goto bad_param; } + + // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic + if (!*service->type_as_string || !MakeDomainNameFromDNSNameString(&service->type, service->type_as_string)) + { LogMsg("ERROR: handle_regservice_request - service->type_as_string bad %s", service->type_as_string); goto bad_param; } + + if (!name[0]) + { + service->name = (gmDNS)->nicelabel; + service->autoname = mDNStrue; + } + else + { + // If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel + if ((flags & kDNSServiceFlagsNoAutoRename) == 0) + { + int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL); + name[newlen] = 0; + } + if (!MakeDomainLabelFromLiteralString(&service->name, name)) + { LogMsg("ERROR: handle_regservice_request - name bad %s", name); goto bad_param; } + service->autoname = mDNSfalse; + } + + if (*domain) + { + service->default_domain = mDNSfalse; + if (!MakeDomainNameFromDNSNameString(&d, domain)) + { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); goto bad_param; } + } + else + { + service->default_domain = mDNStrue; + MakeDomainNameFromDNSNameString(&d, "local."); + } + + if (!ConstructServiceName(&srv, &service->name, &service->type, &d)) + { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", service->name.c, service->type.c, d.c); goto bad_param; } + + if (!MakeDomainNameFromDNSNameString(&service->host, host)) + { LogMsg("ERROR: handle_regservice_request - host bad %s", host); goto bad_param; } + service->autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0; + service->allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0; + + // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with + // a port number of zero. When two instances of the protected client are allowed to run on one + // machine, we don't want to see misleading "Bogus client" messages in syslog and the console. + if (service->port.NotAnInteger) + { + int count = CountExistingRegistrations(&srv, service->port); + if (count) + LogMsg("Client application registered %d identical instances of service %##s port %u.", + count+1, srv.c, mDNSVal16(service->port)); + } + + LogOperation("%3d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", \"%s\", %u) START", + request->sd, name, service->type_as_string, domain, host, mDNSVal16(service->port)); + result = register_service_instance(request, &d); + + if (!result && !*domain) + { + DNameListElem *ptr, *def_domains = mDNSPlatformGetRegDomainList(); + for (ptr = def_domains; ptr; ptr = ptr->next) + register_service_instance(request, &ptr->name); + // note that we don't report errors for non-local, non-explicit domains + mDNS_FreeDNameList(def_domains); + } + +finish: + deliver_error(request, result); + if (result != mStatus_NoError) + { + abort_request(request); + unlink_request(request); + } + else + reset_connected_rstate(request); // prepare to receive add/remove messages + + return; + +bad_param: + //if (service) freeL("service_info", service); Don't think we should do this -- abort_request will free it a second time and crash + deliver_error(request, mStatus_BadParamErr); + abort_request(request); + unlink_request(request); + } + +// service registration callback performs three duties - frees memory for deregistered services, +// handles name conflicts, and delivers completed registration information to the client (via +// process_service_registraion()) + +mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) + { + mStatus err; + mDNSBool SuppressError = mDNSfalse; + service_instance *instance = srs->ServiceContext; + (void)m; // Unused + if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; } + if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; } + + if (instance->request && instance->request->service_registration) + { + service_info *info = instance->request->service_registration; + if (info->default_domain && !instance->default_local) SuppressError = mDNStrue; + // don't send errors up to client for wide-area, empty-string registrations + } + + if (result == mStatus_NoError) + LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED ", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port)); + else if (result == mStatus_MemFree) + LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port)); + else if (result == mStatus_NameConflict) + LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port)); + else + LogOperation("%3d: DNSServiceRegister(%##s, %u) CALLBACK %d", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), result); + + if (result == mStatus_NoError) + { + request_state *req = instance->request; + if (instance->allowremotequery) + { + ExtraResourceRecord *e; + srs->RR_ADV.AllowRemoteQuery = mDNStrue; + srs->RR_PTR.AllowRemoteQuery = mDNStrue; + srs->RR_SRV.AllowRemoteQuery = mDNStrue; + srs->RR_TXT.AllowRemoteQuery = mDNStrue; + for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue; + } + + if (!req) LogMsg("ERROR: regservice_callback - null request object"); + else + { + reply_state *rep; + if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep) != mStatus_NoError) + LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", req->sd, srs->RR_SRV.resrec.name->c); + else + { + transfer_state send_result = send_msg(rep); + if (send_result == t_error || send_result == t_terminated) + { abort_request(req); unlink_request(req); freeL("reply_state", rep); } + else if (send_result == t_complete) freeL("regservice_callback", rep); + else append_reply(req, rep); + } + } + if (instance->autoname && CountPeerRegistrations(m, srs) == 0) + RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately + return; + } + else if (result == mStatus_MemFree) + { + if (instance->rename_on_memfree) + { + instance->rename_on_memfree = 0; + instance->name = gmDNS->nicelabel; + err = mDNS_RenameAndReregisterService(gmDNS, srs, &instance->name); + if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %ld", err); + // error should never happen - safest to log and continue + } + else + { + free_service_instance(instance); + return; + } + } + else if (result == mStatus_NameConflict) + { + if (instance->autoname && CountPeerRegistrations(m, srs) == 0) + { + // On conflict for an autoname service, rename and reregister *all* autoname services + IncrementLabelSuffix(&m->nicelabel, mDNStrue); + m->MainCallback(m, mStatus_ConfigChanged); + } + else if (instance->autoname || instance->autorename) + { + mDNS_RenameAndReregisterService(gmDNS, srs, mDNSNULL); + return; + } + else + { + request_state *rs = instance->request; + if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; } + free_service_instance(instance); + if (!SuppressError && deliver_async_error(rs, reg_service_reply_op, result) < 0) + { + abort_request(rs); + unlink_request(rs); + } + return; + } + } + else + { + request_state *rs = instance->request; + if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; } + if (result != mStatus_NATTraversal) LogMsg("ERROR: unknown result in regservice_callback: %ld", result); + free_service_instance(instance); + if (!SuppressError && deliver_async_error(rs, reg_service_reply_op, result) < 0) + { + abort_request(rs); + unlink_request(rs); + } + return; + } + } + +mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result) + { + ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext; + (void)m; //unused + + if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; } + + debugf("%##s: MemFree", rr->resrec.name->c); + if (rr->resrec.rdata != &rr->rdatastorage) + freeL("Extra RData", rr->resrec.rdata); + freeL("ExtraResourceRecord", extra); + } + +mDNSlocal mStatus add_record_to_service(request_state *rstate, service_instance *instance, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl) + { + ServiceRecordSet *srs = &instance->srs; + ExtraResourceRecord *extra; + mStatus result; + int size; + + if (rdlen > sizeof(RDataBody)) size = rdlen; + else size = sizeof(RDataBody); + + extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); + if (!extra) + { + my_perror("ERROR: malloc"); + return mStatus_NoMemoryErr; + } + + bzero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd + extra->r.resrec.rrtype = rrtype; + extra->r.rdatastorage.MaxRDLength = (mDNSu16) size; + extra->r.resrec.rdlength = rdlen; + memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen); + + result = mDNS_AddRecordToService(gmDNS, srs , extra, &extra->r.rdatastorage, ttl); + if (result) { freeL("ExtraResourceRecord", extra); return result; } + + extra->ClientID = rstate->hdr.reg_index; + return result; + } + +mDNSlocal mStatus handle_add_request(request_state *rstate) + { + uint32_t ttl; + uint16_t rrtype, rdlen; + char *ptr, *rdata; + mStatus result = mStatus_UnknownErr; + DNSServiceFlags flags; + service_info *srvinfo = rstate->service_registration; + service_instance *i; + + if (!srvinfo) { LogMsg("handle_add_request called with NULL service_registration"); return(-1); } + + ptr = rstate->msgdata; + flags = get_flags(&ptr); + rrtype = get_short(&ptr); + rdlen = get_short(&ptr); + rdata = get_rdata(&ptr, rdlen); + ttl = get_long(&ptr); + + if (!ttl) ttl = DefaultTTLforRRType(rrtype); + + LogOperation("%3d: DNSServiceAddRecord(%##s, %s)", rstate->sd, + (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype)); + + for (i = srvinfo->instances; i; i = i->next) + { + result = add_record_to_service(rstate, i, rrtype, rdlen, rdata, ttl); + if (result && i->default_local) break; + else result = mStatus_NoError; // suppress non-local default errors + } + + return(result); + } + +mDNSlocal mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32_t ttl) + { + int rdsize; + RData *newrd; + mStatus result; + + if (rdlen > sizeof(RDataBody)) rdsize = rdlen; + else rdsize = sizeof(RDataBody); + newrd = mallocL("handle_update_request", sizeof(RData) - sizeof(RDataBody) + rdsize); + if (!newrd) FatalError("ERROR: malloc"); + newrd->MaxRDLength = (mDNSu16) rdsize; + memcpy(&newrd->u, rdata, rdlen); + + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; } + + result = mDNS_Update(gmDNS, rr, ttl, rdlen, newrd, update_callback); + if (result) { LogMsg("ERROR: mDNS_Update - %ld", result); freeL("handle_update_request", newrd); } + return result; + } + +mDNSlocal mStatus handle_update_request(request_state *rstate) + { + uint16_t rdlen; + char *ptr, *rdata; + uint32_t ttl; + mStatus result = mStatus_BadReferenceErr; + service_info *srvinfo = rstate->service_registration; + service_instance *i; + AuthRecord *rr = NULL; + + // get the message data + ptr = rstate->msgdata; + get_flags(&ptr); // flags unused + rdlen = get_short(&ptr); + rdata = get_rdata(&ptr, rdlen); + ttl = get_long(&ptr); + + if (rstate->reg_recs) + { + // update an individually registered record + registered_record_entry *reptr; + for (reptr = rstate->reg_recs; reptr; reptr = reptr->next) + { + if (reptr->key == rstate->hdr.reg_index) + { + result = update_record(reptr->rr, rdlen, rdata, ttl); + goto end; + } + } + result = mStatus_BadReferenceErr; + goto end; + } + + // update a record from a service record set + if (!srvinfo) { result = mStatus_BadReferenceErr; goto end; } + for (i = srvinfo->instances; i; i = i->next) + { + if (rstate->hdr.reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT; + else + { + ExtraResourceRecord *e; + for (e = i->srs.Extras; e; e = e->next) + if (e->ClientID == rstate->hdr.reg_index) { rr = &e->r; break; } + } + + if (!rr) { result = mStatus_BadReferenceErr; goto end; } + result = update_record(rr, rdlen, rdata, ttl); + if (result && i->default_local) goto end; + else result = mStatus_NoError; // suppress non-local default errors + } + +end: + LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", rstate->sd, + (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL, + rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>"); + + return(result); + } + +mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd) + { + (void)m; // Unused + if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd); + } + +mDNSlocal void free_service_instance(service_instance *srv) + { + request_state *rstate = srv->request; + ExtraResourceRecord *e = srv->srs.Extras, *tmp; + + // clear pointers from parent struct + if (rstate) + { + service_instance *ptr = rstate->service_registration->instances, *prev = NULL; + while (ptr) + { + if (ptr == srv) + { + if (prev) prev->next = ptr->next; + else rstate->service_registration->instances = ptr->next; + break; + } + prev = ptr; + ptr = ptr->next; + } + } + + while(e) + { + e->r.RecordContext = e; + tmp = e; + e = e->next; + FreeExtraRR(gmDNS, &tmp->r, mStatus_MemFree); + } + + if (srv->subtypes) { freeL("regservice_callback", srv->subtypes); srv->subtypes = NULL; } + freeL("regservice_callback", srv); + } + +mDNSlocal void regservice_termination_callback(void *context) + { + service_info *info = context; + service_instance *i, *p; + if (!info) { LogMsg("regservice_termination_callback context is NULL"); return; } + if (!info->request) { LogMsg("regservice_termination_callback info->request is NULL"); return; } + i = info->instances; + while (i) + { + p = i; + i = i->next; + p->request = NULL; // clear back pointer + // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p) + LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP", info->request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port)); + if (mDNS_DeregisterService(gmDNS, &p->srs)) free_service_instance(p); + } + info->request->service_registration = NULL; // clear pointer from request back to info + if (info->txtdata) { freeL("txtdata", info->txtdata); info->txtdata = NULL; } + freeL("service_info", info); + } + +mDNSlocal mStatus handle_regrecord_request(request_state *rstate) + { + AuthRecord *rr; + registered_record_entry *re; + mStatus result; + + if (rstate->ts != t_complete) + { + LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete"); + abort_request(rstate); + unlink_request(rstate); + return(-1); + } + + rr = read_rr_from_ipc_msg(rstate->msgdata, 1, 1); + if (!rr) return(mStatus_BadParamErr); + + // allocate registration entry, link into list + re = mallocL("handle_regrecord_request", sizeof(registered_record_entry)); + if (!re) FatalError("ERROR: malloc"); + re->key = rstate->hdr.reg_index; + re->rr = rr; + re->rstate = rstate; + re->client_context = rstate->hdr.client_context; + rr->RecordContext = re; + rr->RecordCallback = regrecord_callback; + re->next = rstate->reg_recs; + rstate->reg_recs = re; + + if (!rstate->terminate) + { + rstate->terminate = connected_registration_termination; + rstate->termination_context = rstate; + } + + if (rr->resrec.rroriginalttl == 0) + rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); + + LogOperation("%3d: DNSServiceRegisterRecord %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec)); + result = mDNS_Register(gmDNS, rr); + return(result); + } + +mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result) + { + registered_record_entry *re = rr->RecordContext; + request_state *rstate = re ? re->rstate : NULL; + int len; + reply_state *reply; + transfer_state ts; + (void)m; // Unused + + if (!re) + { + // parent struct alreadt freed by termination callback + if (!result) LogMsg("Error: regrecord_callback: successful registration of orphaned record"); + else + { + if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result); + freeL("regrecord_callback", rr); + } + return; + } + + // format result, add to the list for the request, including the client context in the header + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); //interfaceIndex + len += sizeof(DNSServiceErrorType); + + reply = create_reply(reg_record_reply_op, len, rstate); + reply->mhdr->client_context = re->client_context; + reply->rhdr->flags = dnssd_htonl(0); + reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID)); + reply->rhdr->error = dnssd_htonl(result); + + if (result) + { + // unlink from list, free memory + registered_record_entry **ptr = &re->rstate->reg_recs; + while (*ptr && (*ptr) != re) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; } + *ptr = (*ptr)->next; + freeL("regrecord_callback", re->rr); + re->rr = rr = NULL; + freeL("regrecord_callback", re); + re = NULL; + } + + ts = send_msg(reply); + + if (ts == t_error || ts == t_terminated) { abort_request(rstate); unlink_request(rstate); } + else if (ts == t_complete) freeL("regrecord_callback", reply); + else if (ts == t_morecoming) append_reply(rstate, reply); // client is blocked, link reply into list + } + +mDNSlocal void connected_registration_termination(void *context) + { + int shared; + registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs; + while(ptr) + { + fptr = ptr; + ptr = ptr->next; + shared = fptr->rr->resrec.RecordType == kDNSRecordTypeShared; + fptr->rr->RecordContext = NULL; + mDNS_Deregister(gmDNS, fptr->rr); + freeL("connected_registration_termination", fptr); + } + } + +mDNSlocal mStatus handle_removerecord_request(request_state *rstate) + { + mStatus err = mStatus_BadReferenceErr; + char *ptr; + service_info *srvinfo = rstate->service_registration; + + ptr = rstate->msgdata; + get_flags(&ptr); // flags unused + + if (rstate->reg_recs) err = remove_record(rstate); // remove individually registered record + else if (!srvinfo) LogOperation("%3d: DNSServiceRemoveRecord (bad ref)", rstate->sd); + else + { + service_instance *i; + LogOperation("%3d: DNSServiceRemoveRecord(%##s)", rstate->sd, + (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL); + for (i = srvinfo->instances; i; i = i->next) + { + err = remove_extra(rstate, i); + if (err && i->default_local) break; + else err = mStatus_NoError; // suppress non-local default errors + } + } + + return(err); + } + +// remove a resource record registered via DNSServiceRegisterRecord() +mDNSlocal mStatus remove_record(request_state *rstate) + { + int shared; + mStatus err = mStatus_UnknownErr; + registered_record_entry *e, **ptr = &rstate->reg_recs; + + while(*ptr && (*ptr)->key != rstate->hdr.reg_index) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("DNSServiceRemoveRecord - bad reference"); return mStatus_BadReferenceErr; } + e = *ptr; + *ptr = e->next; // unlink + + LogOperation("%3d: DNSServiceRemoveRecord(%#s)", rstate->sd, e->rr->resrec.name->c); + shared = e->rr->resrec.RecordType == kDNSRecordTypeShared; + e->rr->RecordContext = NULL; + err = mDNS_Deregister(gmDNS, e->rr); + if (err) + { + LogMsg("ERROR: remove_record, mDNS_Deregister: %ld", err); + freeL("remove_record", e->rr); + freeL("remove_record", e); + } + return err; + } + +mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv) + { + mStatus err = mStatus_BadReferenceErr; + ExtraResourceRecord *ptr; + + for (ptr = serv->srs.Extras; ptr; ptr = ptr->next) + { + if (ptr->ClientID == rstate->hdr.reg_index) // found match + return mDNS_RemoveRecordFromService(gmDNS, &serv->srs, ptr, FreeExtraRR, ptr); + } + return err; + } + +// domain enumeration +mDNSlocal void handle_enum_request(request_state *rstate) + { + DNSServiceFlags flags; + uint32_t ifi; + mDNSInterfaceID InterfaceID; + char *ptr = rstate->msgdata; + domain_enum_t *def, *all; + enum_termination_t *term; + mStatus err; + int result; + + if (rstate->ts != t_complete) + { + LogMsg("ERROR: handle_enum_request - transfer state != t_complete"); + abort_request(rstate); + unlink_request(rstate); + return; + } + + flags = get_flags(&ptr); + ifi = get_long(&ptr); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi); + if (ifi && !InterfaceID) + { + deliver_error(rstate, mStatus_BadParamErr); + abort_request(rstate); + unlink_request(rstate); + return; + } + + // allocate context structures + def = mallocL("handle_enum_request", sizeof(domain_enum_t)); + all = mallocL("handle_enum_request", sizeof(domain_enum_t)); + term = mallocL("handle_enum_request", sizeof(enum_termination_t)); + if (!def || !all || !term) FatalError("ERROR: malloc"); + +#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS) + dDNS_RegisterSearchDomains( gmDNS ); +#endif + + // enumeration requires multiple questions, so we must link all the context pointers so that + // necessary context can be reached from the callbacks + def->rstate = rstate; + all->rstate = rstate; + term->def = def; + term->all = all; + term->rstate = rstate; + rstate->termination_context = term; + rstate->terminate = enum_termination_callback; + def->question.QuestionContext = def; + def->type = (flags & kDNSServiceFlagsRegistrationDomains) ? + mDNS_DomainTypeRegistrationDefault: mDNS_DomainTypeBrowseDefault; + all->question.QuestionContext = all; + all->type = (flags & kDNSServiceFlagsRegistrationDomains) ? + mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; + + // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list. + if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly; + + // make the calls + LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", rstate->sd, flags, + (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" : + (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>"); + err = mDNS_GetDomains(gmDNS, &all->question, all->type, NULL, InterfaceID, enum_result_callback, all); + if (err == mStatus_NoError) + err = mDNS_GetDomains(gmDNS, &def->question, def->type, NULL, InterfaceID, enum_result_callback, def); + result = deliver_error(rstate, err); // send error *before* returning local domain + + if (result < 0 || err) + { + abort_request(rstate); + unlink_request(rstate); + return; + } + } + +mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + char domain[MAX_ESCAPED_DOMAIN_NAME]; + domain_enum_t *de = question->QuestionContext; + DNSServiceFlags flags = 0; + reply_state *reply; + (void)m; // Unused + + if (answer->rrtype != kDNSType_PTR) return; + if (!AddRecord && de->type != mDNS_DomainTypeBrowse) return; + + if (AddRecord) + { + flags |= kDNSServiceFlagsAdd; + if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault) + flags |= kDNSServiceFlagsDefault; + } + ConvertDomainNameToCString(&answer->rdata->u.name, domain); + // note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from + // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the + // network, so we just pass kDNSServiceInterfaceIndexAny + reply = format_enumeration_reply(de->rstate, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError); + if (!reply) + { + LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); + return; + } + reply->next = NULL; + append_reply(de->rstate, reply); + return; + } + +mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err) + { + size_t len; + reply_state *reply; + char *data; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + len += sizeof(DNSServiceErrorType); + len += strlen(domain) + 1; + + reply = create_reply(enumeration_reply_op, len, rstate); + reply->rhdr->flags = dnssd_htonl(flags); + reply->rhdr->ifi = dnssd_htonl(ifi); + reply->rhdr->error = dnssd_htonl(err); + data = reply->sdata; + put_string(domain, &data); + return reply; + } + +mDNSlocal void enum_termination_callback(void *context) + { + enum_termination_t *t = context; + mDNS *coredata = gmDNS; + + mDNS_StopGetDomains(coredata, &t->all->question); + mDNS_StopGetDomains(coredata, &t->def->question); + freeL("enum_termination_callback", t->all); + freeL("enum_termination_callback", t->def); + t->rstate->termination_context = NULL; + freeL("enum_termination_callback", t); + } + +mDNSlocal void handle_reconfirm_request(request_state *rstate) + { + AuthRecord *rr = read_rr_from_ipc_msg(rstate->msgdata, 0, 0); + if (rr) + { + mStatus status = mDNS_ReconfirmByValue(gmDNS, &rr->resrec); + LogOperation( + (status == mStatus_NoError) ? + "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" : + "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d", + rstate->sd, RRDisplayString(gmDNS, &rr->resrec), + mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID), status); + status = 0; // Adding this line eliminates a build failure when building mDNSPosix on Tiger + } + abort_request(rstate); + unlink_request(rstate); + freeL("handle_reconfirm_request", rr); + } + +// setup rstate to accept new reg/dereg requests +mDNSlocal void reset_connected_rstate(request_state *rstate) + { + rstate->ts = t_morecoming; + rstate->hdr_bytes = 0; + rstate->data_bytes = 0; + if (rstate->msgbuf) freeL("reset_connected_rstate", rstate->msgbuf); + rstate->msgbuf = NULL; + rstate->bufsize = 0; + } + +// returns a resource record (allocated w/ malloc) containing the data found in an IPC message +// data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl +// (ttl only extracted/set if ttl argument is non-zero). returns NULL for a bad-parameter error +mDNSlocal AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int GetTTL, int validate_flags) + { + char *rdata, name[256]; + AuthRecord *rr; + DNSServiceFlags flags; + uint32_t interfaceIndex; + uint16_t type, class, rdlen; + int storage_size; + + flags = get_flags(&msgbuf); + if (validate_flags && + !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) && + !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)) + { + LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)"); + return NULL; + } + + interfaceIndex = get_long(&msgbuf); + if (get_string(&msgbuf, name, 256) < 0) + { + LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); + return NULL; + } + type = get_short(&msgbuf); + class = get_short(&msgbuf); + rdlen = get_short(&msgbuf); + + if (rdlen > sizeof(RDataBody)) storage_size = rdlen; + else storage_size = sizeof(RDataBody); + + rr = mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size); + if (!rr) FatalError("ERROR: malloc"); + bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd + + mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex), + type, 0, (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), mDNSNULL, mDNSNULL); + + if (!MakeDomainNameFromDNSNameString(rr->resrec.name, name)) + { + LogMsg("ERROR: bad name: %s", name); + freeL("read_rr_from_ipc_msg", rr); + return NULL; + } + + if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue; + rr->resrec.rrclass = class; + rr->resrec.rdlength = rdlen; + rr->resrec.rdata->MaxRDLength = rdlen; + rdata = get_rdata(&msgbuf, rdlen); + memcpy(rr->resrec.rdata->u.data, rdata, rdlen); + if (GetTTL) rr->resrec.rroriginalttl = get_long(&msgbuf); + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + return rr; + } + +mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain) + { + domainlabel n; + domainname d, t; + + if (!MakeDomainLabelFromLiteralString(&n, name)) return -1; + if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1; + if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1; + if (!ConstructServiceName(srv, &n, &t, &d)) return -1; + return 0; + } + +// append a reply to the list in a request object +mDNSlocal void append_reply(request_state *req, reply_state *rep) + { + reply_state *ptr; + + if (!req->replies) req->replies = rep; + else + { + ptr = req->replies; + while (ptr->next) ptr = ptr->next; + ptr->next = rep; + } + rep->next = NULL; + } + +// read_msg may be called any time when the transfer state (rs->ts) is t_morecoming. +// returns the current state of the request (morecoming, error, complete, terminated.) +// if there is no data on the socket, the socket will be closed and t_terminated will be returned +mDNSlocal int read_msg(request_state *rs) + { + uint32_t nleft; + int nread; + char buf[4]; // dummy for death notification + + if (rs->ts == t_terminated || rs->ts == t_error) + { + LogMsg("ERROR: read_msg called with transfer state terminated or error"); + rs->ts = t_error; + return t_error; + } + + if (rs->ts == t_complete) + { // this must be death or something is wrong + nread = recv(rs->sd, buf, 4, 0); + if (!nread) { rs->ts = t_terminated; return t_terminated; } + if (nread < 0) goto rerror; + LogMsg("ERROR: read data from a completed request."); + rs->ts = t_error; + return t_error; + } + + if (rs->ts != t_morecoming) + { + LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs->ts); + rs->ts = t_error; + return t_error; + } + + if (rs->hdr_bytes < sizeof(ipc_msg_hdr)) + { + nleft = sizeof(ipc_msg_hdr) - rs->hdr_bytes; + nread = recv(rs->sd, (char *)&rs->hdr + rs->hdr_bytes, nleft, 0); + if (nread == 0) { rs->ts = t_terminated; return t_terminated; } + if (nread < 0) goto rerror; + rs->hdr_bytes += nread; + if (rs->hdr_bytes == sizeof(ipc_msg_hdr)) + { + ConvertHeaderBytes(&rs->hdr); + if (rs->hdr.version != VERSION) + { + LogMsg("ERROR: read_msg - client version 0x%08X does not match daemon version 0x%08X", rs->hdr.version, VERSION); + rs->ts = t_error; + return t_error; + } + } + if (rs->hdr_bytes > sizeof(ipc_msg_hdr)) + { + LogMsg("ERROR: read_msg - read too many header bytes"); + rs->ts = t_error; + return t_error; + } + } + + // only read data if header is complete + if (rs->hdr_bytes == sizeof(ipc_msg_hdr)) + { + if (rs->hdr.datalen == 0) // ok in removerecord requests + { + rs->ts = t_complete; + rs->msgbuf = NULL; + return t_complete; + } + + if (!rs->msgbuf) // allocate the buffer first time through + { + rs->msgbuf = mallocL("read_msg", rs->hdr.datalen + MSG_PAD_BYTES); + if (!rs->msgbuf) + { + my_perror("ERROR: malloc"); + rs->ts = t_error; + return t_error; + } + rs->msgdata = rs->msgbuf; + bzero(rs->msgbuf, rs->hdr.datalen + MSG_PAD_BYTES); + } + nleft = rs->hdr.datalen - rs->data_bytes; + nread = recv(rs->sd, rs->msgbuf + rs->data_bytes, nleft, 0); + if (nread == 0) { rs->ts = t_terminated; return t_terminated; } + if (nread < 0) goto rerror; + rs->data_bytes += nread; + if (rs->data_bytes > rs->hdr.datalen) + { + LogMsg("ERROR: read_msg - read too many data bytes"); + rs->ts = t_error; + return t_error; + } + } + + if (rs->hdr_bytes == sizeof(ipc_msg_hdr) && rs->data_bytes == rs->hdr.datalen) + rs->ts = t_complete; + else rs->ts = t_morecoming; + + return rs->ts; + +rerror: + if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return t_morecoming; + my_perror("ERROR: read_msg"); + rs->ts = t_error; + return t_error; + } + +mDNSlocal int send_msg(reply_state *rs) + { + ssize_t nwriten; + + if (!rs->msgbuf) + { + LogMsg("ERROR: send_msg called with NULL message buffer"); + return t_error; + } + + if (rs->request->no_reply) //!!!KRS this behavior should be optimized if it becomes more common + { + rs->ts = t_complete; + freeL("send_msg", rs->msgbuf); + return t_complete; + } + + ConvertHeaderBytes(rs->mhdr); + nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0); + ConvertHeaderBytes(rs->mhdr); + if (nwriten < 0) + { + if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) nwriten = 0; + else + { +#if !defined(PLATFORM_NO_EPIPE) + if (dnssd_errno() == EPIPE) + { + debugf("%3d: broken pipe", rs->sd); + rs->ts = t_terminated; + rs->request->ts = t_terminated; + return t_terminated; + } + else +#endif + { + my_perror("ERROR: send\n"); + rs->ts = t_error; + return t_error; + } + } + } + rs->nwriten += nwriten; + + if (rs->nwriten == rs->len) + { + rs->ts = t_complete; + freeL("send_msg", rs->msgbuf); + } + return rs->ts; + } + +mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request) + { + reply_state *reply; + int totallen; + + if ((unsigned)datalen < sizeof(reply_hdr)) + { + LogMsg("ERROR: create_reply - data length less than lenght of required fields"); + return NULL; + } + + totallen = (int) (datalen + sizeof(ipc_msg_hdr)); + reply = mallocL("create_reply", sizeof(reply_state)); + if (!reply) FatalError("ERROR: malloc"); + bzero(reply, sizeof(reply_state)); + reply->ts = t_morecoming; + reply->sd = request->sd; + reply->request = request; + reply->len = totallen; + reply->msgbuf = mallocL("create_reply", totallen); + if (!reply->msgbuf) FatalError("ERROR: malloc"); + bzero(reply->msgbuf, totallen); + reply->mhdr = (ipc_msg_hdr *)reply->msgbuf; + reply->rhdr = (reply_hdr *)(reply->msgbuf + sizeof(ipc_msg_hdr)); + reply->sdata = reply->msgbuf + sizeof(ipc_msg_hdr) + sizeof(reply_hdr); + reply->mhdr->version = VERSION; + reply->mhdr->op = op; + reply->mhdr->datalen = totallen - sizeof(ipc_msg_hdr); + return reply; + } + +mDNSlocal int deliver_error(request_state *rstate, mStatus err) + { + int nwritten = -1; + undelivered_error_t *undeliv; + + err = dnssd_htonl(err); + nwritten = send(rstate->sd, (dnssd_sockbuf_t) &err, sizeof(mStatus), 0); + if (nwritten < (int)sizeof(mStatus)) + { + if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) + nwritten = 0; + if (nwritten < 0) + { + my_perror("ERROR: send - unable to deliver error to client"); + return(-1); + } + else + { + //client blocked - store result and come backr + undeliv = mallocL("deliver_error", sizeof(undelivered_error_t)); + if (!undeliv) FatalError("ERROR: malloc"); + undeliv->err = err; + undeliv->nwritten = nwritten; + undeliv->sd = rstate->sd; + rstate->u_err = undeliv; + return 0; + } + } + return 0; + } + +// returns 0 on success, -1 if send is incomplete, or on terminal failure (request is aborted) +mDNSlocal transfer_state send_undelivered_error(request_state *rs) + { + int nwritten; + + nwritten = send(rs->u_err->sd, (char *)(&rs->u_err->err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0); + if (nwritten < 0) + { + if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) + nwritten = 0; + else + { + my_perror("ERROR: send - unable to deliver error to client\n"); + return t_error; + } + } + if ((unsigned int)(nwritten + rs->u_err->nwritten) >= sizeof(mStatus)) + { + freeL("send_undelivered_error", rs->u_err); + rs->u_err = NULL; + return t_complete; + } + rs->u_err->nwritten += nwritten; + return t_morecoming; + } + +// send bogus data along with an error code to the app callback +// returns 0 on success (linking reply into list of not fully delivered), +// -1 on failure (request should be aborted) +mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err) + { + int len; + reply_state *reply; + transfer_state ts; + + if (rs->no_reply) return 0; + len = 256; // long enough for any reply handler to read all args w/o buffer overrun + reply = create_reply(op, len, rs); + reply->rhdr->error = dnssd_htonl(err); + ts = send_msg(reply); + if (ts == t_error || ts == t_terminated) + { + freeL("deliver_async_error", reply); + return -1; + } + else if (ts == t_complete) freeL("deliver_async_error", reply); + else if (ts == t_morecoming) append_reply(rs, reply); // client is blocked, link reply into list + return 0; + } + +mDNSlocal void abort_request(request_state *rs) + { + reply_state *rep, *ptr; + + if (rs->terminate) rs->terminate(rs->termination_context); // terminate field may not be set yet + if (rs->msgbuf) freeL("abort_request", rs->msgbuf); + LogOperation("%3d: Removing FD", rs->sd); + udsSupportRemoveFDFromEventLoop(rs->sd); // Note: This also closes file descriptor rs->sd for us + + // Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses + // for detecting when the memory for an object is inadvertently freed while the object is still on some list + rs->sd = -2; + + // free pending replies + rep = rs->replies; + while(rep) + { + if (rep->msgbuf) freeL("abort_request", rep->msgbuf); + ptr = rep; + rep = rep->next; + freeL("abort_request", ptr); + } + + if (rs->u_err) + { + freeL("abort_request", rs->u_err); + rs->u_err = NULL; + } + } + +mDNSlocal void unlink_request(request_state *rs) + { + request_state *ptr; + + if (rs == all_requests) + { + all_requests = all_requests->next; + freeL("unlink_request", rs); + return; + } + for(ptr = all_requests; ptr->next; ptr = ptr->next) + if (ptr->next == rs) + { + ptr->next = rs->next; + freeL("unlink_request", rs); + return; + } + } + +//hack to search-replace perror's to LogMsg's +mDNSlocal void my_perror(char *errmsg) + { + LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno())); + } + +// check that the message delivered by the client is sufficiently long to extract the required data from the buffer +// without overrunning it. +// returns 0 on success, -1 on error. + +mDNSlocal int validate_message(request_state *rstate) + { + uint32_t min_size; + + switch(rstate->hdr.op) + { + case resolve_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + (3 * sizeof(char)); // name, regtype, domain + break; + case query_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + sizeof(char) + // fullname + (2 * sizeof(uint16_t)); // type, class + break; + case browse_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + (2 * sizeof(char)); // regtype, domain + break; + case reg_service_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + (4 * sizeof(char)) + // name, type, domain, host + (2 * sizeof(uint16_t)); // port, textlen + break; + case enumeration_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t); // interface + break; + case reg_record_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + sizeof(char) + // fullname + (3 * sizeof(uint16_t)) + // type, class, rdlen + sizeof(uint32_t); // ttl + break; + case add_record_request: min_size = sizeof(DNSServiceFlags) + // flags + (2 * sizeof(uint16_t)) + // type, rdlen + sizeof(uint32_t); // ttl + break; + case update_record_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint16_t) + // rdlen + sizeof(uint32_t); // ttl + break; + case remove_record_request: min_size = sizeof(DNSServiceFlags); // flags + break; + case reconfirm_record_request: min_size=sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + sizeof(char) + // fullname + (3 * sizeof(uint16_t)); // type, class, rdlen + break; + case setdomain_request: min_size = sizeof(DNSServiceFlags) + sizeof(char); // flags + domain + break; + default: + LogMsg("ERROR: validate_message - unsupported request type: %d", rstate->hdr.op); + return -1; + } + + return (rstate->data_bytes >= min_size ? 0 : -1); + + } + +mDNSlocal uint32_t dnssd_htonl(uint32_t l) + { + uint32_t ret; + char * data; + + data = (char*) &ret; + + put_long(l, &data); + + return ret; + } + +#if defined(_WIN32) + +mDNSlocal char * win32_strerror(int inErrorCode) + { + static char buffer[1024]; + DWORD n; + + memset(buffer, 0, sizeof(buffer)); + + n = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + (DWORD) inErrorCode, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + buffer, + sizeof( buffer ), + NULL ); + + if( n > 0 ) + { + // Remove any trailing CR's or LF's since some messages have them. + + while( ( n > 0 ) && isspace( ( (unsigned char *) buffer)[ n - 1 ] ) ) + { + buffer[ --n ] = '\0'; + } + } + + return buffer; + } + +#endif diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h new file mode 100644 index 0000000000..9dec0288b7 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h @@ -0,0 +1,118 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + File: uds_daemon.h + + Contains: Interfaces necessary to talk to uds_daemon.c. + + Version: 1.0 + + Change History (most recent first): + +$Log: uds_daemon.h,v $ +Revision 1.15 2006/08/14 23:24:57 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.14 2005/01/27 17:48:39 cheshire +Added comment about CFSocketInvalidate closing the underlying socket + +Revision 1.13 2004/12/10 05:27:26 cheshire +<rdar://problem/3909147> Guard against multiple autoname services of the same type on the same machine + +Revision 1.12 2004/12/10 04:28:28 cheshire +<rdar://problem/3914406> User not notified of name changes for services using new UDS API + +Revision 1.11 2004/12/06 21:15:23 ksekar +<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.10 2004/10/26 04:31:44 cheshire +Rename CountSubTypes() as ChopSubTypes() + +Revision 1.9 2004/09/30 00:25:00 ksekar +<rdar://problem/3695802> Dynamically update default registration domains on config change + +Revision 1.8 2004/09/21 21:05:11 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.7 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.6 2004/08/11 01:58:49 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init() + +Revision 1.5 2004/06/18 04:44:58 rpantos +Use platform layer for socket types + +Revision 1.4 2004/06/12 00:51:58 cheshire +Changes for Windows compatibility + +Revision 1.3 2004/01/25 00:03:21 cheshire +Change to use mDNSVal16() instead of private PORT_AS_NUM() macro + +Revision 1.2 2004/01/24 08:46:26 bradley +Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs. + +Revision 1.1 2003/12/08 21:11:42 rpantos; +Changes necessary to support mDNSResponder on Linux. + +*/ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mDNSEmbeddedAPI.h" +#include "dnssd_ipc.h" + + +/* Client interface: */ + +#define SRS_PORT(S) mDNSVal16((S)->RR_SRV.resrec.rdata->u.srv.port) + +extern int udsserver_init(void); + +// takes the next scheduled event time, does idle work, and returns the updated nextevent time +extern mDNSs32 udsserver_idle(mDNSs32 nextevent); + +extern void udsserver_info(mDNS *const m); // print out info about current state + +extern void udsserver_handle_configchange(void); + +extern int udsserver_exit(void); // should be called prior to app exit + +extern void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add); +extern void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add); + +/* Routines that uds_daemon expects to link against: */ + +typedef void (*udsEventCallback)(void *context); + +extern mStatus udsSupportAddFDToEventLoop(dnssd_sock_t fd, udsEventCallback callback, void *context); +extern mStatus udsSupportRemoveFDFromEventLoop(dnssd_sock_t fd); // Note: This also CLOSES the file descriptor as well + +// RecordUpdatedNiceLabel() can be a no-op on platforms that don't care about updating the machine's +// global default service name (was OS X calls the "Computer Name") in response to name conflicts. +extern void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay); + +// Globals and functions defined in uds_daemon.c and also shared with the old "daemon.c" on OS X +extern mDNS mDNSStorage; +extern mDNSs32 ChopSubTypes(char *regtype); +extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p); +extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port); +extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result); +extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h index e9274319f2..28464d5a4c 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h @@ -204,7 +204,7 @@ extern char *getportname(int, in_port_t); extern void interpret_arp(int, struct arphdr *, int); extern void interpret_bparam(int, int, int, int, int, char *, int); -extern void interpret_dns(int, int, const uchar_t *, int); +extern void interpret_dns(int, int, const uchar_t *, int, int); extern void interpret_mount(int, int, int, int, int, char *, int); extern void interpret_nfs(int, int, int, int, int, char *, int); extern void interpret_nfs3(int, int, int, int, int, char *, int); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c index 736b4dd52b..309d24a429 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c @@ -67,7 +67,7 @@ static size_t print_domain_name(char *line, const uchar_t *header, const uchar_t *data, const uchar_t *data_end); void -interpret_dns(int flags, int proto, const uchar_t *data, int len) +interpret_dns(int flags, int proto, const uchar_t *data, int len, int port) { typedef HEADER dns_header; dns_header header; @@ -76,12 +76,25 @@ interpret_dns(int flags, int proto, const uchar_t *data, int len) ushort_t count; const uchar_t *rrp; /* Resource Record Pointer. */ const uchar_t *data_end; + const char *protostr; + char *protopfxstr; + char *protohdrstr; if (proto == IPPROTO_TCP) { /* not supported now */ return; } + if (port == IPPORT_DOMAIN) { + protostr = "DNS"; + protopfxstr = "DNS: "; + protohdrstr = "DNS Header"; + } else { + protostr = "MDNS"; + protopfxstr = "MDNS: "; + protohdrstr = "MDNS Header"; + } + /* We need at least the header in order to parse a packet. */ if (sizeof (dns_header) > len) { return; @@ -100,7 +113,8 @@ interpret_dns(int flags, int proto, const uchar_t *data, int len) if (flags & F_SUM) { line = get_sum_line(); - line += sprintf(line, "DNS %c ", header.qr ? 'R' : 'C'); + line += sprintf(line, "%s %c ", + protostr, header.qr ? 'R' : 'C'); if (header.qr) { /* answer */ @@ -135,7 +149,7 @@ interpret_dns(int flags, int proto, const uchar_t *data, int len) } } if (flags & F_DTAIL) { - show_header("DNS: ", "DNS Header", sizeof (dns_header)); + show_header(protopfxstr, protohdrstr, sizeof (dns_header)); show_space(); if (header.qr) { /* answer */ @@ -348,6 +362,15 @@ print_question(char *line, const uchar_t *header, const uchar_t *data, GETINT16(type, data); GETINT16(cls, data); + /* + * Multicast DNS re-uses the top bit of the class field + * in the question and answer sections. Unicast DNS only + * uses 1 (Internet), 3 and 4. Hence it is safe. The top + * order bit is always cleared here to display the rrclass in case + * of Multicast DNS packets. + */ + cls = cls & 0x7fff; + if (detail) { (void) snprintf(get_line(0, 0), get_line_remain(), DNS_INDENT "Class: %u (%s)", @@ -470,10 +493,10 @@ print_answer(char *line, const uchar_t *header, const uchar_t *data, */ if ((data_end - data) < ((ptrdiff_t)(sizeof (size) - + sizeof (xrcode) - + sizeof (ver) - + sizeof (cls) /* zero */ - + sizeof (rdlen)))) { + + sizeof (xrcode) + + sizeof (ver) + + sizeof (cls) /* zero */ + + sizeof (rdlen)))) { return (data_end - data_bak); } @@ -533,13 +556,22 @@ print_answer(char *line, const uchar_t *header, const uchar_t *data, */ if ((data_end - data) < ((ptrdiff_t)(sizeof (cls) - + sizeof (ttl) - + sizeof (rdlen)))) { + + sizeof (ttl) + + sizeof (rdlen)))) { return (data_end - data_bak); } GETINT16(cls, data); + /* + * Multicast DNS re-uses the top bit of the class field + * in the question and answer sections. Unicast DNS only + * uses 1 (Internet), 3 and 4. Hence it is safe. The top + * order bit is always cleared here to display the rrclass in case + * of Multicast DNS packets. + */ + cls = cls & 0x7fff; + if (detail) { (void) snprintf(get_line(0, 0), get_line_remain(), DNS_INDENT "Class: %d (%s)", cls, diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c index f79202abab..83bb72f519 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c @@ -55,6 +55,7 @@ static const struct porttable pt_udp[] = { { IPPORT_TIMESERVER, "TIME" }, { IPPORT_NAMESERVER, "NAME" }, { IPPORT_DOMAIN, "DNS" }, + { IPPORT_MDNS, "MDNS" }, { IPPORT_BOOTPS, "BOOTPS" }, { IPPORT_BOOTPC, "BOOTPC" }, { IPPORT_TFTP, "TFTP" }, @@ -307,8 +308,8 @@ interpret_syslog(int flags, char dir, int port, const char *syslogstr, priostrlen, syslogstr, bogus ? "" : " ", facilstr, pristr); (void) snprintf(get_line(0, 0), get_line_remain(), - "\"%s\"", - show_string(syslogstr, dlen, 60)); + "\"%s\"", + show_string(syslogstr, dlen, 60)); show_trailer(); } } @@ -354,9 +355,10 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst, which = dst; } - if ((dst == IPPORT_DOMAIN || src == IPPORT_DOMAIN) && + if ((dst == IPPORT_DOMAIN || src == IPPORT_DOMAIN || + dst == IPPORT_MDNS || src == IPPORT_MDNS) && proto != IPPROTO_TCP) { - interpret_dns(flags, proto, (uchar_t *)data, dlen); + interpret_dns(flags, proto, (uchar_t *)data, dlen, which); return (1); } @@ -435,9 +437,9 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst, if (flags & F_SUM) { (void) snprintf(get_sum_line(), MAXLINE, - "%s %c port=%d %s", - pn, dir, port, - show_string(data, dlen, 20)); + "%s %c port=%d %s", + pn, dir, port, + show_string(data, dlen, 20)); } if (flags & F_DTAIL) { @@ -446,8 +448,8 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst, show_header(pbuff, hbuff, dlen); show_space(); (void) snprintf(get_line(0, 0), get_line_remain(), - "\"%s\"", - show_string(data, dlen, 60)); + "\"%s\"", + show_string(data, dlen, 60)); show_trailer(); } return (1); diff --git a/usr/src/cmd/netfiles/nsswitch.dns b/usr/src/cmd/netfiles/nsswitch.dns index c545f0b57b..ff4bafcc0c 100644 --- a/usr/src/cmd/netfiles/nsswitch.dns +++ b/usr/src/cmd/netfiles/nsswitch.dns @@ -18,10 +18,10 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" +#ident "%Z%%M% %I% %E% SMI" # # /etc/nsswitch.dns: @@ -39,12 +39,13 @@ passwd: files group: files # You must also set up the /etc/resolv.conf file for DNS name -# server lookup. See resolv.conf(4). -hosts: files dns +# server lookup. See resolv.conf(4). For lookup via mdns +# svc:/network/dns/multicast:default must also be enabled. See mdnsd(1M) +hosts: files dns mdns # Note that IPv4 addresses are searched for in all of the ipnodes databases # before searching the hosts databases. -ipnodes: files dns +ipnodes: files dns mdns networks: files protocols: files diff --git a/usr/src/cmd/nscd/nscd_cfgdef.h b/usr/src/cmd/nscd/nscd_cfgdef.h index 2d0fa99926..73722ab972 100644 --- a/usr/src/cmd/nscd/nscd_cfgdef.h +++ b/usr/src/cmd/nscd/nscd_cfgdef.h @@ -122,23 +122,25 @@ nscd_cfg_id_t _nscd_cfg_nsw_src[] = { { 1, "ldap" }, { 2, "nis" }, { 3, "nisplus" }, - { 4, "dns" }, - { 5, "compat" }, - { 6, "user" }, + { 4, "mdns" }, + { 5, "dns" }, + { 6, "compat" }, + { 7, "user" }, { -1, NULL } }; /* * name service related smf service table * (the order of the services should match the order of the source - * listed above, 0: files, 1: ldap, 2: nis, 3: nisplus. dns is - * not needed) + * listed above, 0: files, 1: ldap, 2: nis, 3: nisplus, 4: mdns. + * dns is not needed) */ nscd_cfg_id_t _nscd_cfg_smf_services[] = { { 0, "svc:/system/name-service-cache:default"}, { 1, "svc:/network/ldap/client:default" }, { 2, "svc:/network/nis/client:default" }, { 3, "svc:/network/rpc/bind:default" }, + { 4, "svc:/network/dns/multicast:default" }, { -1, NULL } }; diff --git a/usr/src/cmd/nscd/nscd_switch.c b/usr/src/cmd/nscd/nscd_switch.c index 365fffa084..311bc60aaa 100644 --- a/usr/src/cmd/nscd/nscd_switch.c +++ b/usr/src/cmd/nscd/nscd_switch.c @@ -494,80 +494,112 @@ get_gss_func(void **func_p) return (NSCD_SUCCESS); } +/* + * get_dns_funcs returns pointers to gethostbyname functions in the + * dynamically loaded nss_dns & nss_mdns modules that return host + * lookup results along with the TTL value in the DNS resource + * records. The dnsi parameter indicates whether the lookup database + * is hosts(0) or ipnodes(1). The srcname parameter identifies the DNS + * module: dns/mdns and the function returns the address of the specific + * gethostbyname function in func_p variable. + */ static nscd_rc_t -get_dns_funcs(int dnsi, void **func_p) +get_dns_funcs(int dnsi, nss_status_t (**func_p)(), const char *srcname) { char *me = "get_dns_funcs"; - static void *handle = NULL; - static mutex_t func_lock = DEFAULTMUTEX; - void *sym; - char *func_name[2] = { "_nss_get_dns_hosts_name", - "_nss_get_dns_ipnodes_name" }; - static void *func[2] = {NULL, NULL}; + int si; + static void *handle[2] = { NULL, NULL }; + static mutex_t func_lock[2] = { DEFAULTMUTEX, DEFAULTMUTEX }; + void *sym[2]; + static void *func[2][2] = {{NULL, NULL}, {NULL, NULL}}; + static const char *lib[2] = { "nss_dns.so.1", "nss_mdns.so.1" }; + static const char *func_name[2][2] = + {{ "_nss_get_dns_hosts_name", "_nss_get_dns_ipnodes_name" }, + { "_nss_get_mdns_hosts_name", "_nss_get_mdns_ipnodes_name" }}; + + /* source index: 0 = dns, 1 = mdns */ + if (strcmp(srcname, "dns") == 0) + si = 0; + else + si = 1; - if (handle != NULL && dnsi > 0 && func[dnsi] != NULL) { - (void) memcpy(func_p, &func[dnsi], sizeof (void *)); + /* + * function index (func[si][dnsi]): + * [0,0] = dns/hosts, [0,1] = dns/ipnodes, + * [1,0] = mdns/hosts, [1,1] = mdns/ipnodes + */ + + if (handle[si] != NULL && dnsi >= 0 && func[si][dnsi] != NULL) { + *func_p = (nss_status_t (*)()) func[si][dnsi]; return (NSCD_SUCCESS); } - (void) mutex_lock(&func_lock); + (void) mutex_lock(&func_lock[si]); /* close the handle if requested */ if (dnsi < 0) { - if (handle != NULL) { - (void) dlclose(handle); - func[0] = NULL; - func[1] = NULL; + if (handle[si] != NULL) { + (void) dlclose(handle[si]); + func[si][0] = NULL; + func[si][1] = NULL; } - (void) mutex_unlock(&func_lock); + (void) mutex_unlock(&func_lock[si]); return (NSCD_SUCCESS); } - if (handle != NULL && func[dnsi] != NULL) { - (void) memcpy(func_p, &func[dnsi], sizeof (void *)); - (void) mutex_unlock(&func_lock); + if (handle[si] != NULL && func[si][dnsi] != NULL) { + *func_p = (nss_status_t (*)()) func[si][dnsi]; + (void) mutex_unlock(&func_lock[si]); return (NSCD_SUCCESS); } - if (handle == NULL) { - handle = dlopen("nss_dns.so.1", RTLD_LAZY); - if (handle == NULL) { + if (handle[si] == NULL) { + handle[si] = dlopen(lib[si], RTLD_LAZY); + if (handle[si] == NULL) { _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR) - (me, "unable to dlopen nss_dns.so.1\n"); - (void) mutex_unlock(&func_lock); + (me, "unable to dlopen %s\n", lib[si]); + (void) mutex_unlock(&func_lock[si]); return (NSCD_CFG_DLOPEN_ERROR); } } - if ((sym = dlsym(handle, func_name[dnsi])) == NULL) { + if ((sym[si] = dlsym(handle[si], func_name[si][dnsi])) == NULL) { _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR) - (me, "unable to find symbol %s\n", func_name[dnsi]); - (void) mutex_unlock(&func_lock); + (me, "unable to find symbol %s\n", func_name[si][dnsi]); + (void) mutex_unlock(&func_lock[si]); return (NSCD_CFG_DLSYM_ERROR); } else { - (void) memcpy(func_p, &sym, sizeof (void *)); - (void) memcpy(&func[dnsi], &sym, sizeof (void *)); + *func_p = (nss_status_t (*)()) sym[si]; + func[si][dnsi] = sym[si]; } - (void) mutex_unlock(&func_lock); + (void) mutex_unlock(&func_lock[si]); return (NSCD_SUCCESS); } static nss_status_t -search_dns_withttl(nscd_sw_return_t *swret, char *srcname, int dnsi) +search_dns_withttl(nscd_sw_return_t *swret, const char *srcname, int dnsi) { nss_status_t (*func)(); nss_status_t res = NSS_UNAVAIL; nscd_rc_t rc; swret->noarg = 0; - if (strcmp(srcname, "dns") != 0) + if (strcmp(srcname, "dns") != 0 && strcmp(srcname, "mdns") != 0) return (NSS_ERROR); - rc = get_dns_funcs(dnsi, (void **)&func); - if (rc == NSCD_SUCCESS) + rc = get_dns_funcs(dnsi, &func, srcname); + if (rc == NSCD_SUCCESS) { + /* + * data_len in the packed buf header may be changed + * by the dns or mdns backend, reset it just in + * case + */ + ((nss_pheader_t *)swret->pbuf)->data_len = + swret->datalen; res = (func)(NULL, &swret->pbuf, &swret->pbufsiz); + } return (res); } @@ -1279,6 +1311,7 @@ nss_psearch(void *buffer, size_t length) (void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp)); swret.pbuf = buffer; swret.pbufsiz = length; + swret.datalen = pbuf->data_len; /* * use the generic nscd_initf for all database lookups diff --git a/usr/src/cmd/nscd/nscd_switch.h b/usr/src/cmd/nscd/nscd_switch.h index add7a6a26e..3e2e1ba926 100644 --- a/usr/src/cmd/nscd/nscd_switch.h +++ b/usr/src/cmd/nscd/nscd_switch.h @@ -201,6 +201,7 @@ typedef struct { int errnum; /* errno from the backend */ int noarg; /* if set, backend does not use the arg structure */ int fallback; /* if set, may need to fall back to main nscd */ + int datalen; /* pbuf->data_len (backend may change it) */ } nscd_sw_return_t; /* diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 44a056019e..f509c72a1f 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -103,6 +103,7 @@ SUBDIRS += \ libdlpi \ libeti \ libcrypt \ + libdns_sd \ libefi \ libfstyp \ libwanboot \ @@ -345,6 +346,7 @@ HDRSUBDIRS= \ libdhcpsvc \ libdhcputil \ libdisasm \ + libdns_sd \ libdtrace \ libdtrace_jni \ libeti \ diff --git a/usr/src/lib/libdns_sd/Makefile b/usr/src/lib/libdns_sd/Makefile new file mode 100644 index 0000000000..21a4e1aa1d --- /dev/null +++ b/usr/src/lib/libdns_sd/Makefile @@ -0,0 +1,59 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.lib + +HDR = dns_sd.h +HDRDIR = common +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all install: install_h $(SUBDIRS) .WAIT java + +clean clobber: $(SUBDIRS) + +ROOTHDRDIR= $(ROOT)/usr/include +ROOTHDRS= $(HDR:%=$(ROOTHDRDIR)/%) + +install_h: $(ROOTHDRS) + +check: + +lint: $(SUBDIRS) + +$(SUBDIRS) java: FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/libdns_sd/Makefile.com b/usr/src/lib/libdns_sd/Makefile.com new file mode 100644 index 0000000000..4b6fe00f9b --- /dev/null +++ b/usr/src/lib/libdns_sd/Makefile.com @@ -0,0 +1,51 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = libdns_sd.a +VERS = .1 +OBJECTS = dnssd_clientlib.o dnssd_clientstub.o dnssd_ipc.o + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) + +SRCDIR = ../common + +LDLIBS += -lsocket -lc + +C99MODE = $(C99_ENABLE) +CFLAGS += -erroff=E_ASSIGNMENT_TYPE_MISMATCH +CPPFLAGS += -I$(SRCDIR) -DNOT_HAVE_SA_LEN + +.PARALLEL = $(OBJECTS) +.KEEP_STATE: + +lint: lintcheck + +all: $(LIBS) + +include ../../Makefile.targ diff --git a/usr/src/lib/libdns_sd/README b/usr/src/lib/libdns_sd/README new file mode 100644 index 0000000000..a7deae9108 --- /dev/null +++ b/usr/src/lib/libdns_sd/README @@ -0,0 +1,65 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +#ident "%Z%%M% %I% %E% SMI" +# + +Multicast DNS and Service Discovery support in Solaris using the +Apple Bonjour source code (v107.6). Apple Bonjour source can be +downloaded from: + http://developer.apple.com/networking/bonjour/download/ +The following components are integrated from the Apple Bonjour +source in Solaris: + libdns_sd: usr/src/lib/libdns_sd <dns_sd.h> + libjdns_sd: usr/src/lib/libdns_sd/java/common + dnssd.jar: usr/src/lib/libdns_sd/java/com (incl. examples) + mdnsd: usr/src/cmd/cmd-inet/usr.lib/mdnsd + dns-sd: usr/src/cmd/cmd-inet/usr.bin/dns-sd.c + +Following fixes have been made to the Apple Bonjour source +integrated in Solaris: +* 64-bit support by adding pad bytes in ipc_msg_hdr_struct +* 64-bit support in libjdns_sd, dnssd.jar (JNISupport.c, DNSSD.java) +* mdnsd switches to user 'noaccess' and not 'nobody' after init +* Fixes to support IPv6 (mDNSPosix.c, mDNSUNP.c) +* Fix error raised when uDNS.c is compiled with Sun Studio compiler +* Fix in dnssd_clientstub.c to not check errno when recvmsg returns 0 +* mDNSDebug.c modified to not send msgs directly to console when + syslog call returns an error. Logs the messages at LOG_INFO level + and not LOG_ERR + +In addition the project introduces the following changes: +* A new nss_mdns module is introduced to use Multicast DNS (mdns) + for resolving link-local hostnames and is located at: + usr/src/lib/nsswitch/mdns +* snoop updated to decode mDNS packets +* updated /etc/services to include mdns +* <netinet/in.h> updated to include mdns +* svc:/network/dns/multicast:default introduced to manage mDNS daemon +* solaris.smf.manage.mdns & solaris.smf.value.mdns authorizations + to modify nss_mdns configuration in svc:/network/dns/multicast:default + Both authorizations added in network management execution profile. +* Default nsswitch.dns includes mdns as source for hosts & ipnodes +* nscd daemon updated to support mdns +* SUNWdsdu and SUNWdsdr packages deliver all the new mDNS + service discovery components. diff --git a/usr/src/lib/libdns_sd/amd64/Makefile b/usr/src/lib/libdns_sd/amd64/Makefile new file mode 100644 index 0000000000..2fa1329472 --- /dev/null +++ b/usr/src/lib/libdns_sd/amd64/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../Makefile.lib.64 + +CFLAGS64 += -erroff=E_ASSIGNMENT_TYPE_MISMATCH +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libdns_sd/common/dns_sd.h b/usr/src/lib/libdns_sd/common/dns_sd.h new file mode 100644 index 0000000000..7ffe6da198 --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dns_sd.h @@ -0,0 +1,1725 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _DNS_SD_H +#define _DNS_SD_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* standard calling convention under Win32 is __stdcall */ +/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ +/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ +#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64) +#define DNSSD_API __stdcall +#else +#define DNSSD_API +#endif + +/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */ +#if defined(__FreeBSD__) && (__FreeBSD__ < 5) +#include <sys/types.h> + +/* Likewise, on Sun, standard integer types are in sys/types.h */ +#elif defined(__sun__) +#include <sys/types.h> + +/* EFI does not have stdint.h, or anything else equivalent */ +#elif defined(EFI32) || defined(EFI64) +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; + +/* Windows has its own differences */ +#elif defined(_WIN32) +#include <windows.h> +#define _UNUSED +#define bzero(a, b) memset(a, 0, b) +#ifndef _MSL_STDINT_H +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; +#endif + +/* All other Posix platforms use stdint.h */ +#else +#include <stdint.h> +#include <strings.h> +#endif + +/* DNSServiceRef, DNSRecordRef + * + * Opaque internal data types. + * Note: client is responsible for serializing access to these structures if + * they are shared between concurrent threads. + */ + +typedef struct _DNSServiceRef_t *DNSServiceRef; +typedef struct _DNSRecordRef_t *DNSRecordRef; + +/* General flags used in functions defined below */ +enum + { + kDNSServiceFlagsMoreComing = 0x1, + /* MoreComing indicates to a callback that at least one more result is + * queued and will be delivered following immediately after this one. + * Applications should not update their UI to display browse + * results when the MoreComing flag is set, because this would + * result in a great deal of ugly flickering on the screen. + * Applications should instead wait until until MoreComing is not set, + * and then update their UI. + * When MoreComing is not set, that doesn't mean there will be no more + * answers EVER, just that there are no more answers immediately + * available right now at this instant. If more answers become available + * in the future they will be delivered as usual. + */ + + kDNSServiceFlagsAdd = 0x2, + kDNSServiceFlagsDefault = 0x4, + /* Flags for domain enumeration and browse/query reply callbacks. + * "Default" applies only to enumeration and is only valid in + * conjuction with "Add". An enumeration callback with the "Add" + * flag NOT set indicates a "Remove", i.e. the domain is no longer + * valid. + */ + + kDNSServiceFlagsNoAutoRename = 0x8, + /* Flag for specifying renaming behavior on name conflict when registering + * non-shared records. By default, name conflicts are automatically handled + * by renaming the service. NoAutoRename overrides this behavior - with this + * flag set, name conflicts will result in a callback. The NoAutorename flag + * is only valid if a name is explicitly specified when registering a service + * (i.e. the default name is not used.) + */ + + kDNSServiceFlagsShared = 0x10, + kDNSServiceFlagsUnique = 0x20, + /* Flag for registering individual records on a connected + * DNSServiceRef. Shared indicates that there may be multiple records + * with this name on the network (e.g. PTR records). Unique indicates that the + * record's name is to be unique on the network (e.g. SRV records). + */ + + kDNSServiceFlagsBrowseDomains = 0x40, + kDNSServiceFlagsRegistrationDomains = 0x80, + /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains. + * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains + * enumerates domains recommended for registration. + */ + + kDNSServiceFlagsLongLivedQuery = 0x100, + /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */ + + kDNSServiceFlagsAllowRemoteQuery = 0x200, + /* Flag for creating a record for which we will answer remote queries + * (queries from hosts more than one hop away; hosts not directly connected to the local link). + */ + + kDNSServiceFlagsForceMulticast = 0x400, + /* Flag for signifying that a query or registration should be performed exclusively via multicast DNS, + * even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS. + */ + + kDNSServiceFlagsReturnCNAME = 0x800 + /* Flag for returning CNAME records in the DNSServiceQueryRecord call. CNAME records are + * normally followed without indicating to the client that there was a CNAME record. + */ + }; + +/* + * The values for DNS Classes and Types are listed in RFC 1035, and are available + * on every OS in its DNS header file. Unfortunately every OS does not have the + * same header file containing DNS Class and Type constants, and the names of + * the constants are not consistent. For example, BIND 8 uses "T_A", + * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc. + * For this reason, these constants are also listed here, so that code using + * the DNS-SD programming APIs can use these constants, so that the same code + * can compile on all our supported platforms. + */ + +enum + { + kDNSServiceClass_IN = 1 /* Internet */ + }; + +enum + { + kDNSServiceType_A = 1, /* Host address. */ + kDNSServiceType_NS = 2, /* Authoritative server. */ + kDNSServiceType_MD = 3, /* Mail destination. */ + kDNSServiceType_MF = 4, /* Mail forwarder. */ + kDNSServiceType_CNAME = 5, /* Canonical name. */ + kDNSServiceType_SOA = 6, /* Start of authority zone. */ + kDNSServiceType_MB = 7, /* Mailbox domain name. */ + kDNSServiceType_MG = 8, /* Mail group member. */ + kDNSServiceType_MR = 9, /* Mail rename name. */ + kDNSServiceType_NULL = 10, /* Null resource record. */ + kDNSServiceType_WKS = 11, /* Well known service. */ + kDNSServiceType_PTR = 12, /* Domain name pointer. */ + kDNSServiceType_HINFO = 13, /* Host information. */ + kDNSServiceType_MINFO = 14, /* Mailbox information. */ + kDNSServiceType_MX = 15, /* Mail routing information. */ + kDNSServiceType_TXT = 16, /* One or more text strings. */ + kDNSServiceType_RP = 17, /* Responsible person. */ + kDNSServiceType_AFSDB = 18, /* AFS cell database. */ + kDNSServiceType_X25 = 19, /* X_25 calling address. */ + kDNSServiceType_ISDN = 20, /* ISDN calling address. */ + kDNSServiceType_RT = 21, /* Router. */ + kDNSServiceType_NSAP = 22, /* NSAP address. */ + kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ + kDNSServiceType_SIG = 24, /* Security signature. */ + kDNSServiceType_KEY = 25, /* Security key. */ + kDNSServiceType_PX = 26, /* X.400 mail mapping. */ + kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ + kDNSServiceType_AAAA = 28, /* IPv6 Address. */ + kDNSServiceType_LOC = 29, /* Location Information. */ + kDNSServiceType_NXT = 30, /* Next domain (security). */ + kDNSServiceType_EID = 31, /* Endpoint identifier. */ + kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ + kDNSServiceType_SRV = 33, /* Server Selection. */ + kDNSServiceType_ATMA = 34, /* ATM Address */ + kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ + kDNSServiceType_KX = 36, /* Key Exchange */ + kDNSServiceType_CERT = 37, /* Certification record */ + kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */ + kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ + kDNSServiceType_SINK = 40, /* Kitchen sink (experimentatl) */ + kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ + kDNSServiceType_TKEY = 249, /* Transaction key */ + kDNSServiceType_TSIG = 250, /* Transaction signature. */ + kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ + kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ + kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ + kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ + kDNSServiceType_ANY = 255 /* Wildcard match. */ + }; + + +/* possible error code values */ +enum + { + kDNSServiceErr_NoError = 0, + kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ + kDNSServiceErr_NoSuchName = -65538, + kDNSServiceErr_NoMemory = -65539, + kDNSServiceErr_BadParam = -65540, + kDNSServiceErr_BadReference = -65541, + kDNSServiceErr_BadState = -65542, + kDNSServiceErr_BadFlags = -65543, + kDNSServiceErr_Unsupported = -65544, + kDNSServiceErr_NotInitialized = -65545, + kDNSServiceErr_AlreadyRegistered = -65547, + kDNSServiceErr_NameConflict = -65548, + kDNSServiceErr_Invalid = -65549, + kDNSServiceErr_Firewall = -65550, + kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ + kDNSServiceErr_BadInterfaceIndex = -65552, + kDNSServiceErr_Refused = -65553, + kDNSServiceErr_NoSuchRecord = -65554, + kDNSServiceErr_NoAuth = -65555, + kDNSServiceErr_NoSuchKey = -65556, + kDNSServiceErr_NATTraversal = -65557, + kDNSServiceErr_DoubleNAT = -65558, + kDNSServiceErr_BadTime = -65559 + /* mDNS Error codes are in the range + * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ + }; + + +/* Maximum length, in bytes, of a service name represented as a */ +/* literal C-String, including the terminating NULL at the end. */ + +#define kDNSServiceMaxServiceName 64 + +/* Maximum length, in bytes, of a domain name represented as an *escaped* C-String */ +/* including the final trailing dot, and the C-String terminating NULL at the end. */ + +#define kDNSServiceMaxDomainName 1005 + +/* + * Notes on DNS Name Escaping + * -- or -- + * "Why is kDNSServiceMaxDomainName 1005, when the maximum legal domain name is 255 bytes?" + * + * All strings used in DNS-SD are UTF-8 strings. + * With few exceptions, most are also escaped using standard DNS escaping rules: + * + * '\\' represents a single literal '\' in the name + * '\.' represents a single literal '.' in the name + * '\ddd', where ddd is a three-digit decimal value from 000 to 255, + * represents a single literal byte with that value. + * A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain. + * + * The exceptions, that do not use escaping, are the routines where the full + * DNS name of a resource is broken, for convenience, into servicename/regtype/domain. + * In these routines, the "servicename" is NOT escaped. It does not need to be, since + * it is, by definition, just a single literal string. Any characters in that string + * represent exactly what they are. The "regtype" portion is, technically speaking, + * escaped, but since legal regtypes are only allowed to contain letters, digits, + * and hyphens, there is nothing to escape, so the issue is moot. The "domain" + * portion is also escaped, though most domains in use on the public Internet + * today, like regtypes, don't contain any characters that need to be escaped. + * As DNS-SD becomes more popular, rich-text domains for service discovery will + * become common, so software should be written to cope with domains with escaping. + * + * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String + * terminating NULL at the end). The regtype is of the form _service._tcp or + * _service._udp, where the "service" part is 1-14 characters, which may be + * letters, digits, or hyphens. The domain part of the three-part name may be + * any legal domain, providing that the resulting servicename+regtype+domain + * name does not exceed 255 bytes. + * + * For most software, these issues are transparent. When browsing, the discovered + * servicenames should simply be displayed as-is. When resolving, the discovered + * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve(). + * When a DNSServiceResolve() succeeds, the returned fullname is already in + * the correct format to pass to standard system DNS APIs such as res_query(). + * For converting from servicename/regtype/domain to a single properly-escaped + * full DNS name, the helper function DNSServiceConstructFullName() is provided. + * + * The following (highly contrived) example illustrates the escaping process. + * Suppose you have an service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp" + * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com." + * The full (escaped) DNS name of this service's SRV record would be: + * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com. + */ + + +/* + * Constants for specifying an interface index + * + * Specific interface indexes are identified via a 32-bit unsigned integer returned + * by the if_nametoindex() family of calls. + * + * If the client passes 0 for interface index, that means "do the right thing", + * which (at present) means, "if the name is in an mDNS local multicast domain + * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast + * on all applicable interfaces, otherwise send via unicast to the appropriate + * DNS server." Normally, most clients will use 0 for interface index to + * automatically get the default sensible behaviour. + * + * If the client passes a positive interface index, then for multicast names that + * indicates to do the operation only on that one interface. For unicast names the + * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering + * a service, then that service will be found *only* by other local clients + * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly + * or kDNSServiceInterfaceIndexAny. + * If a client has a 'private' service, accessible only to other processes + * running on the same machine, this allows the client to advertise that service + * in a way such that it does not inadvertently appear in service lists on + * all the other machines on the network. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing + * then it will find *all* records registered on that same local machine. + * Clients explicitly wishing to discover *only* LocalOnly services can + * accomplish this by inspecting the interfaceIndex of each service reported + * to their DNSServiceBrowseReply() callback function, and discarding those + * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. + */ + +#define kDNSServiceInterfaceIndexAny 0 +#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) -1 ) + + +typedef uint32_t DNSServiceFlags; +typedef int32_t DNSServiceErrorType; + + +/********************************************************************************************* + * + * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions + * + *********************************************************************************************/ + + +/* DNSServiceRefSockFD() + * + * Access underlying Unix domain socket for an initialized DNSServiceRef. + * The DNS Service Discovery implmementation uses this socket to communicate between + * the client and the mDNSResponder daemon. The application MUST NOT directly read from + * or write to this socket. Access to the socket is provided so that it can be used as a + * run loop source, or in a select() loop: when data is available for reading on the socket, + * DNSServiceProcessResult() should be called, which will extract the daemon's reply from + * the socket, and pass it to the appropriate application callback. By using a run loop or + * select(), results from the daemon can be processed asynchronously. Without using these + * constructs, DNSServiceProcessResult() will block until the response from the daemon arrives. + * The client is responsible for ensuring that the data on the socket is processed in a timely + * fashion - the daemon may terminate its connection with a client that does not clear its + * socket buffer. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + * return value: The DNSServiceRef's underlying socket descriptor, or -1 on + * error. + */ + +int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); + + +/* DNSServiceProcessResult() + * + * Read a reply from the daemon, calling the appropriate application callback. This call will + * block until the daemon's response is received. Use DNSServiceRefSockFD() in + * conjunction with a run loop or select() to determine the presence of a response from the + * server before calling this function to process the reply without blocking. Call this function + * at any point if it is acceptable to block until the daemon's response arrives. Note that the + * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is + * a reply from the daemon - the daemon may terminate its connection with a client that does not + * process the daemon's responses. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls + * that take a callback parameter. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); + + +/* DNSServiceRefDeallocate() + * + * Terminate a connection with the daemon and free memory associated with the DNSServiceRef. + * Any services or records registered with this DNSServiceRef will be deregistered. Any + * Browse, Resolve, or Query operations called with this reference will be terminated. + * + * Note: If the reference's underlying socket is used in a run loop or select() call, it should + * be removed BEFORE DNSServiceRefDeallocate() is called, as this function closes the reference's + * socket. + * + * Note: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs + * created via this reference will be invalidated by this call - the resource records are + * deregistered, and their DNSRecordRefs may not be used in subsequent functions. Similarly, + * if the reference was initialized with DNSServiceRegister, and an extra resource record was + * added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call + * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent + * functions. + * + * Note: This call is to be used only with the DNSServiceRef defined by this API. It is + * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based + * DNSServiceDiscovery.h API. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + */ + +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); + + +/********************************************************************************************* + * + * Domain Enumeration + * + *********************************************************************************************/ + +/* DNSServiceEnumerateDomains() + * + * Asynchronously enumerate domains available for browsing and registration. + * + * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains + * are to be found. + * + * Note that the names returned are (like all of DNS-SD) UTF-8 strings, + * and are escaped using standard DNS escaping rules. + * (See "Notes on DNS Name Escaping" earlier in this file for more details.) + * A graphical browser displaying a hierarchical tree-structured view should cut + * the names at the bare dots to yield individual labels, then de-escape each + * label according to the escaping rules, and then display the resulting UTF-8 text. + * + * DNSServiceDomainEnumReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains(). + * + * flags: Possible values are: + * kDNSServiceFlagsMoreComing + * kDNSServiceFlagsAdd + * kDNSServiceFlagsDefault + * + * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given + * interface is determined via the if_nametoindex() family of calls.) + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates + * the failure that occurred (other parameters are undefined if errorCode is nonzero). + * + * replyDomain: The name of the domain. + * + * context: The context pointer passed to DNSServiceEnumerateDomains. + * + */ + +typedef void (DNSSD_API *DNSServiceDomainEnumReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *replyDomain, + void *context + ); + + +/* DNSServiceEnumerateDomains() Parameters: + * + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the enumeration operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Possible values are: + * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. + * kDNSServiceFlagsRegistrationDomains to enumerate domains recommended + * for registration. + * + * interfaceIndex: If non-zero, specifies the interface on which to look for domains. + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to enumerate domains on + * all interfaces. See "Constants for specifying an interface index" for more details. + * + * callBack: The function to be called when a domain is found or the call asynchronously + * fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context /* may be NULL */ + ); + + +/********************************************************************************************* + * + * Service Registration + * + *********************************************************************************************/ + +/* Register a service that is discovered via Browse() and Resolve() calls. + * + * + * DNSServiceRegisterReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceRegister(). + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts, + * if the kDNSServiceFlagsNoAutoRename flag was used when registering.) + * Other parameters are undefined if errorCode is nonzero. + * + * name: The service name registered (if the application did not specify a name in + * DNSServiceRegister(), this indicates what name was automatically chosen). + * + * regtype: The type of service registered, as it was passed to the callout. + * + * domain: The domain on which the service was registered (if the application did not + * specify a domain in DNSServiceRegister(), this indicates the default domain + * on which the service was registered). + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceRegisterReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context + ); + + +/* DNSServiceRegister() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the registration will remain active indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the service + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to register on all + * available interfaces. See "Constants for specifying an interface index" for more details. + * + * flags: Indicates the renaming behavior on name conflict (most applications + * will pass 0). See flag definitions above for details. + * + * name: If non-NULL, specifies the service name to be registered. + * Most applications will not specify a name, in which case the computer + * name is used (this name is communicated to the client via the callback). + * If a name is specified, it must be 1-63 bytes of UTF-8 text. + * If the name is longer than 63 bytes it will be automatically truncated + * to a legal length, unless the NoAutoRename flag is set, + * in which case kDNSServiceErr_BadParam will be returned. + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). The service type must be an underscore, followed + * by 1-14 characters, which may be letters, digits, or hyphens. + * The transport protocol must be "_tcp" or "_udp". New service types + * should be registered at <http://www.dns-sd.org/ServiceTypes.html>. + * + * domain: If non-NULL, specifies the domain on which to advertise the service. + * Most applications will not specify a domain, instead automatically + * registering in the default domain(s). + * + * host: If non-NULL, specifies the SRV target host name. Most applications + * will not specify a host, instead automatically using the machine's + * default host name(s). Note that specifying a non-NULL host does NOT + * create an address record for that host - the application is responsible + * for ensuring that the appropriate address record exists, or creating it + * via DNSServiceRegisterRecord(). + * + * port: The port, in network byte order, on which the service accepts connections. + * Pass 0 for a "placeholder" service (i.e. a service that will not be discovered + * by browsing, but will cause a name conflict if another client tries to + * register that same name). Most clients will not use placeholder services. + * + * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL. + * + * txtRecord: The TXT record rdata. A non-NULL txtRecord MUST be a properly formatted DNS + * TXT record, i.e. <length byte> <data> <length byte> <data> ... + * Passing NULL for the txtRecord is allowed as a synonym for txtLen=1, txtRecord="", + * i.e. it creates a TXT record of length one containing a single empty string. + * RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty + * string is the smallest legal DNS TXT record. + * As with the other parameters, the DNSServiceRegister call copies the txtRecord + * data; e.g. if you allocated the storage for the txtRecord parameter with malloc() + * then you can safely free that memory right after the DNSServiceRegister call returns. + * + * callBack: The function to be called when the registration completes or asynchronously + * fails. The client MAY pass NULL for the callback - The client will NOT be notified + * of the default values picked on its behalf, and the client will NOT be notified of any + * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration + * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL. + * The client may still deregister the service at any time via DNSServiceRefDeallocate(). + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceRegister + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, /* may be NULL */ + const char *regtype, + const char *domain, /* may be NULL */ + const char *host, /* may be NULL */ + uint16_t port, + uint16_t txtLen, + const void *txtRecord, /* may be NULL */ + DNSServiceRegisterReply callBack, /* may be NULL */ + void *context /* may be NULL */ + ); + + +/* DNSServiceAddRecord() + * + * Add a record to a registered service. The name of the record will be the same as the + * registered service's name. + * The record can later be updated or deregistered by passing the RecordRef initialized + * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * + * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe + * with respect to a single DNSServiceRef. If you plan to have multiple threads + * in your program simultaneously add, update, or remove records from the same + * DNSServiceRef, then it's the caller's responsibility to use a mutext lock + * or take similar appropriate precautions to serialize those calls. + * + * + * Parameters; + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * If the above DNSServiceRef is passed to DNSServiceRefDeallocate(), RecordRef is also + * invalidated and may not be used further. + * + * flags: Currently ignored, reserved for future use. + * + * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc) + * + * rdlen: The length, in bytes, of the rdata. + * + * rdata: The raw rdata to be contained in the added resource record. + * + * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred (the RecordRef is not initialized). + */ + +DNSServiceErrorType DNSSD_API DNSServiceAddRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ); + + +/* DNSServiceUpdateRecord + * + * Update a registered resource record. The record must either be: + * - The primary txt record of a service registered via DNSServiceRegister() + * - A record added to a registered service via DNSServiceAddRecord() + * - An individual record registered by DNSServiceRegisterRecord() + * + * + * Parameters: + * + * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister() + * or DNSServiceCreateConnection(). + * + * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the + * service's primary txt record. + * + * flags: Currently ignored, reserved for future use. + * + * rdlen: The length, in bytes, of the new rdata. + * + * rdata: The new rdata to be contained in the updated resource record. + * + * ttl: The time to live of the updated resource record, in seconds. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, /* may be NULL */ + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ); + + +/* DNSServiceRemoveRecord + * + * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister + * an record registered individually via DNSServiceRegisterRecord(). + * + * Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the + * record being removed was registered via DNSServiceAddRecord()) or by + * DNSServiceCreateConnection() (if the record being removed was registered via + * DNSServiceRegisterRecord()). + * + * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord() + * or DNSServiceRegisterRecord(). + * + * flags: Currently ignored, reserved for future use. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags + ); + + +/********************************************************************************************* + * + * Service Discovery + * + *********************************************************************************************/ + +/* Browse for instances of a service. + * + * + * DNSServiceBrowseReply() Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceBrowse(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd. + * See flag definitions for details. + * + * interfaceIndex: The interface on which the service is advertised. This index should + * be passed to DNSServiceResolve() when resolving the service. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * serviceName: The discovered service name. This name should be displayed to the user, + * and stored for subsequent use in the DNSServiceResolve() call. + * + * regtype: The service type, which is usually (but not always) the same as was passed + * to DNSServiceBrowse(). One case where the discovered service type may + * not be the same as the requested service type is when using subtypes: + * The client may want to browse for only those ftp servers that allow + * anonymous connections. The client will pass the string "_ftp._tcp,_anon" + * to DNSServiceBrowse(), but the type of the service that's discovered + * is simply "_ftp._tcp". The regtype for each discovered service instance + * should be stored along with the name, so that it can be passed to + * DNSServiceResolve() when the service is later resolved. + * + * domain: The domain of the discovered service instance. This may or may not be the + * same as the domain that was passed to DNSServiceBrowse(). The domain for each + * discovered service instance should be stored along with the name, so that + * it can be passed to DNSServiceResolve() when the service is later resolved. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceBrowseReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *serviceName, + const char *regtype, + const char *replyDomain, + void *context + ); + + +/* DNSServiceBrowse() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the browse operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface on which to browse for services + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to browse on all available + * interfaces. See "Constants for specifying an interface index" for more details. + * + * regtype: The service type being browsed for followed by the protocol, separated by a + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * + * domain: If non-NULL, specifies the domain on which to browse for services. + * Most applications will not specify a domain, instead browsing on the + * default domain(s). + * + * callBack: The function to be called when an instance of the service being browsed for + * is found, or if the call asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceBrowse + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, /* may be NULL */ + DNSServiceBrowseReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceResolve() + * + * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and + * txt record. + * + * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use + * DNSServiceQueryRecord() instead, as it is more efficient for this task. + * + * Note: When the desired results have been returned, the client MUST terminate the resolve by calling + * DNSServiceRefDeallocate(). + * + * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record + * and a single TXT record. To resolve non-standard services with multiple SRV or TXT records, + * DNSServiceQueryRecord() should be used. + * + * DNSServiceResolveReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceResolve(). + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: The interface on which the service was resolved. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * fullname: The full service domain name, in the form <servicename>.<protocol>.<domain>. + * (This name is escaped following standard DNS rules, making it suitable for + * passing to standard system DNS APIs such as res_query(), or to the + * special-purpose functions included in this API that take fullname parameters. + * See "Notes on DNS Name Escaping" earlier in this file for more details.) + * + * hosttarget: The target hostname of the machine providing the service. This name can + * be passed to functions like gethostbyname() to identify the host's IP address. + * + * port: The port, in network byte order, on which connections are accepted for this service. + * + * txtLen: The length of the txt record, in bytes. + * + * txtRecord: The service's primary txt record, in standard txt record format. + * + * context: The context pointer that was passed to the callout. + * + * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *" + * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127. + * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings. + * These should be fixed by updating your own callback function definition to match the corrected + * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent + * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250 + * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes. + * If you need to maintain portable code that will compile cleanly with both the old and new versions of + * this header file, you should update your callback function definition to use the correct unsigned value, + * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate + * the compiler warning, e.g.: + * DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context); + * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly) + * with both the old header and with the new corrected version. + * + */ + +typedef void (DNSSD_API *DNSServiceResolveReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + const char *hosttarget, + uint16_t port, + uint16_t txtLen, + const unsigned char *txtRecord, + void *context + ); + + +/* DNSServiceResolve() Parameters + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the resolve operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: The interface on which to resolve the service. If this resolve call is + * as a result of a currently active DNSServiceBrowse() operation, then the + * interfaceIndex should be the index reported in the DNSServiceBrowseReply + * callback. If this resolve call is using information previously saved + * (e.g. in a preference file) for later use, then use interfaceIndex 0, because + * the desired service may now be reachable via a different physical interface. + * See "Constants for specifying an interface index" for more details. + * + * name: The name of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * regtype: The type of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * domain: The domain of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceResolve + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context /* may be NULL */ + ); + + +/********************************************************************************************* + * + * Special Purpose Calls (most applications will not use these) + * + *********************************************************************************************/ + +/* DNSServiceCreateConnection() + * + * Create a connection to the daemon allowing efficient registration of + * multiple individual records. + * + * + * Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * the reference (via DNSServiceRefDeallocate()) severs the + * connection and deregisters all records registered on this connection. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred (in which + * case the DNSServiceRef is not initialized). + */ + +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); + + +/* DNSServiceRegisterRecord + * + * Register an individual resource record on a connected DNSServiceRef. + * + * Note that name conflicts occurring for records registered via this call must be handled + * by the client in the callback. + * + * + * DNSServiceRegisterRecordReply() parameters: + * + * sdRef: The connected DNSServiceRef initialized by + * DNSServiceCreateConnection(). + * + * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above + * DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is + * invalidated, and may not be used further. + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts.) + * Other parameters are undefined if errorCode is nonzero. + * + * context: The context pointer that was passed to the callout. + * + */ + + typedef void (DNSSD_API *DNSServiceRegisterRecordReply) + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + void *context + ); + + +/* DNSServiceRegisterRecord() Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * (To deregister ALL records registered on a single connected DNSServiceRef + * and deallocate each of their corresponding DNSServiceRecordRefs, call + * DNSServiceRefDealloocate()). + * + * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique + * (see flag type definitions for details). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the record + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the record to be registered on all interfaces. + * See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record. + * + * rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN) + * + * rdlen: Length, in bytes, of the rdata. + * + * rdata: A pointer to the raw rdata, as it is to appear in the DNS record. + * + * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails (e.g. because of a name conflict.) + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSRecordRef is + * not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceQueryRecord + * + * Query for an arbitrary DNS record. + * + * + * DNSServiceQueryRecordReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and + * kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records + * with a ttl of 0, i.e. "Remove" events. + * + * interfaceIndex: The interface on which the query was resolved (the index for a given + * interface is determined via the if_nametoindex() family of calls). + * See "Constants for specifying an interface index" for more details. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * errorCode is nonzero. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + * ttl: The resource record's time to live, in seconds. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceQueryRecordReply) + ( + DNSServiceRef DNSServiceRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + void *context + ); + + +/* DNSServiceQueryRecord() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the query operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast + * query in a non-local domain. Without setting this flag, unicast queries + * will be one-shot - that is, only answers available at the time of the call + * will be returned. By setting this flag, answers (including Add and Remove + * events) that become available after the initial call is made will generate + * callbacks. This flag has no effect on link-local multicast queries. + * + * interfaceIndex: If non-zero, specifies the interface on which to issue the query + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the name to be queried for on all + * interfaces. See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record to be queried for. + * + * rrtype: The numerical type of the resource record to be queried for + * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceReconfirmRecord + * + * Instruct the daemon to verify the validity of a resource record that appears to + * be out of date (e.g. because tcp connection to a service's target failed.) + * Causes the record to be flushed from the daemon's cache (as well as all other + * daemons' caches on the network) if the record is determined to be invalid. + * + * Parameters: + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface of the record in question. + * Passing 0 causes all instances of this record to be reconfirmed. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + */ + +DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord + ( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata + ); + + +/********************************************************************************************* + * + * General Utility Functions + * + *********************************************************************************************/ + +/* DNSServiceConstructFullName() + * + * Concatenate a three-part domain name (as returned by the above callbacks) into a + * properly-escaped full domain name. Note that callbacks in the above functions ALREADY ESCAPE + * strings where necessary. + * + * Parameters: + * + * fullName: A pointer to a buffer that where the resulting full domain name is to be written. + * The buffer must be kDNSServiceMaxDomainName (1005) bytes in length to + * accommodate the longest legal domain name without buffer overrun. + * + * service: The service name - any dots or backslashes must NOT be escaped. + * May be NULL (to construct a PTR record name, e.g. + * "_ftp._tcp.apple.com."). + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). + * + * domain: The domain name, e.g. "apple.com.". Literal dots or backslashes, + * if any, must be escaped, e.g. "1st\. Floor.apple.com." + * + * return value: Returns 0 on success, -1 on error. + * + */ + +int DNSSD_API DNSServiceConstructFullName + ( + char *fullName, + const char *service, /* may be NULL */ + const char *regtype, + const char *domain + ); + + +/********************************************************************************************* + * + * TXT Record Construction Functions + * + *********************************************************************************************/ + +/* + * A typical calling sequence for TXT record construction is something like: + * + * Client allocates storage for TXTRecord data (e.g. declare buffer on the stack) + * TXTRecordCreate(); + * TXTRecordSetValue(); + * TXTRecordSetValue(); + * TXTRecordSetValue(); + * ... + * DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... ); + * TXTRecordDeallocate(); + * Explicitly deallocate storage for TXTRecord data (if not allocated on the stack) + */ + + +/* TXTRecordRef + * + * Opaque internal data type. + * Note: Represents a DNS-SD TXT record. + */ + +typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef; + + +/* TXTRecordCreate() + * + * Creates a new empty TXTRecordRef referencing the specified storage. + * + * If the buffer parameter is NULL, or the specified storage size is not + * large enough to hold a key subsequently added using TXTRecordSetValue(), + * then additional memory will be added as needed using malloc(). + * + * On some platforms, when memory is low, malloc() may fail. In this + * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this + * error condition will need to be handled as appropriate by the caller. + * + * You can avoid the need to handle this error condition if you ensure + * that the storage you initially provide is large enough to hold all + * the key/value pairs that are to be added to the record. + * The caller can precompute the exact length required for all of the + * key/value pairs to be added, or simply provide a fixed-sized buffer + * known in advance to be large enough. + * A no-value (key-only) key requires (1 + key length) bytes. + * A key with empty value requires (1 + key length + 1) bytes. + * A key with non-empty value requires (1 + key length + 1 + value length). + * For most applications, DNS-SD TXT records are generally + * less than 100 bytes, so in most cases a simple fixed-sized + * 256-byte buffer will be more than sufficient. + * Recommended size limits for DNS-SD TXT Records are discussed in + * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt> + * + * Note: When passing parameters to and from these TXT record APIs, + * the key name does not include the '=' character. The '=' character + * is the separator between the key and value in the on-the-wire + * packet format; it is not part of either the key or the value. + * + * txtRecord: A pointer to an uninitialized TXTRecordRef. + * + * bufferLen: The size of the storage provided in the "buffer" parameter. + * + * buffer: Optional caller-supplied storage used to hold the TXTRecord data. + * This storage must remain valid for as long as + * the TXTRecordRef. + */ + +void DNSSD_API TXTRecordCreate + ( + TXTRecordRef *txtRecord, + uint16_t bufferLen, + void *buffer + ); + + +/* TXTRecordDeallocate() + * + * Releases any resources allocated in the course of preparing a TXT Record + * using TXTRecordCreate()/TXTRecordSetValue()/TXTRecordRemoveValue(). + * Ownership of the buffer provided in TXTRecordCreate() returns to the client. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + */ + +void DNSSD_API TXTRecordDeallocate + ( + TXTRecordRef *txtRecord + ); + + +/* TXTRecordSetValue() + * + * Adds a key (optionally with value) to a TXTRecordRef. If the "key" already + * exists in the TXTRecordRef, then the current value will be replaced with + * the new value. + * Keys may exist in four states with respect to a given TXT record: + * - Absent (key does not appear at all) + * - Present with no value ("key" appears alone) + * - Present with empty value ("key=" appears in TXT record) + * - Present with non-empty value ("key=value" appears in TXT record) + * For more details refer to "Data Syntax for DNS-SD TXT Records" in + * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt> + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * key: A null-terminated string which only contains printable ASCII + * values (0x20-0x7E), excluding '=' (0x3D). Keys should be + * 8 characters or less (not counting the terminating null). + * + * valueSize: The size of the value. + * + * value: Any binary value. For values that represent + * textual data, UTF-8 is STRONGLY recommended. + * For values that represent textual data, valueSize + * should NOT include the terminating null (if any) + * at the end of the string. + * If NULL, then "key" will be added with no value. + * If non-NULL but valueSize is zero, then "key=" will be + * added with empty value. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_Invalid if the "key" string contains + * illegal characters. + * Returns kDNSServiceErr_NoMemory if adding this key would + * exceed the available storage. + */ + +DNSServiceErrorType DNSSD_API TXTRecordSetValue + ( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, /* may be zero */ + const void *value /* may be NULL */ + ); + + +/* TXTRecordRemoveValue() + * + * Removes a key from a TXTRecordRef. The "key" must be an + * ASCII string which exists in the TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * key: A key name which exists in the TXTRecordRef. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoSuchKey if the "key" does not + * exist in the TXTRecordRef. + */ + +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue + ( + TXTRecordRef *txtRecord, + const char *key + ); + + +/* TXTRecordGetLength() + * + * Allows you to determine the length of the raw bytes within a TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * return value: Returns the size of the raw bytes inside a TXTRecordRef + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + * Returns 0 if the TXTRecordRef is empty. + */ + +uint16_t DNSSD_API TXTRecordGetLength + ( + const TXTRecordRef *txtRecord + ); + + +/* TXTRecordGetBytesPtr() + * + * Allows you to retrieve a pointer to the raw bytes within a TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * return value: Returns a pointer to the raw bytes inside the TXTRecordRef + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + */ + +const void * DNSSD_API TXTRecordGetBytesPtr + ( + const TXTRecordRef *txtRecord + ); + + +/********************************************************************************************* + * + * TXT Record Parsing Functions + * + *********************************************************************************************/ + +/* + * A typical calling sequence for TXT record parsing is something like: + * + * Receive TXT record data in DNSServiceResolve() callback + * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something + * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1); + * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2); + * ... + * bcopy(val1ptr, myval1, len1); + * bcopy(val2ptr, myval2, len2); + * ... + * return; + * + * If you wish to retain the values after return from the DNSServiceResolve() + * callback, then you need to copy the data to your own storage using bcopy() + * or similar, as shown in the example above. + * + * If for some reason you need to parse a TXT record you built yourself + * using the TXT record construction functions above, then you can do + * that using TXTRecordGetLength and TXTRecordGetBytesPtr calls: + * TXTRecordGetValue(TXTRecordGetLength(x), TXTRecordGetBytesPtr(x), key, &len); + * + * Most applications only fetch keys they know about from a TXT record and + * ignore the rest. + * However, some debugging tools wish to fetch and display all keys. + * To do that, use the TXTRecordGetCount() and TXTRecordGetItemAtIndex() calls. + */ + +/* TXTRecordContainsKey() + * + * Allows you to determine if a given TXT Record contains a specified key. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * return value: Returns 1 if the TXT Record contains the specified key. + * Otherwise, it returns 0. + */ + +int DNSSD_API TXTRecordContainsKey + ( + uint16_t txtLen, + const void *txtRecord, + const char *key + ); + + +/* TXTRecordGetValuePtr() + * + * Allows you to retrieve the value for a given key from a TXT Record. + * + * txtLen: The size of the received TXT Record + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * valueLen: On output, will be set to the size of the "value" data. + * + * return value: Returns NULL if the key does not exist in this TXT record, + * or exists with no value (to differentiate between + * these two cases use TXTRecordContainsKey()). + * Returns pointer to location within TXT Record bytes + * if the key exists with empty or non-empty value. + * For empty value, valueLen will be zero. + * For non-empty value, valueLen will be length of value data. + */ + +const void * DNSSD_API TXTRecordGetValuePtr + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + uint8_t *valueLen + ); + + +/* TXTRecordGetCount() + * + * Returns the number of keys stored in the TXT Record. The count + * can be used with TXTRecordGetItemAtIndex() to iterate through the keys. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * return value: Returns the total number of keys in the TXT Record. + * + */ + +uint16_t DNSSD_API TXTRecordGetCount + ( + uint16_t txtLen, + const void *txtRecord + ); + + +/* TXTRecordGetItemAtIndex() + * + * Allows you to retrieve a key name and value pointer, given an index into + * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1. + * It's also possible to iterate through keys in a TXT record by simply + * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero + * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid. + * + * On return: + * For keys with no value, *value is set to NULL and *valueLen is zero. + * For keys with empty value, *value is non-NULL and *valueLen is zero. + * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * index: An index into the TXT Record. + * + * keyBufLen: The size of the string buffer being supplied. + * + * key: A string buffer used to store the key name. + * On return, the buffer contains a null-terminated C string + * giving the key name. DNS-SD TXT keys are usually + * 8 characters or less. To hold the maximum possible + * key name, the buffer should be 256 bytes long. + * + * valueLen: On output, will be set to the size of the "value" data. + * + * value: On output, *value is set to point to location within TXT + * Record bytes that holds the value data. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoMemory if keyBufLen is too short. + * Returns kDNSServiceErr_Invalid if index is greater than + * TXTRecordGetCount()-1. + */ + +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex + ( + uint16_t txtLen, + const void *txtRecord, + uint16_t index, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value + ); + +#ifdef __APPLE_API_PRIVATE + +/* + * Mac OS X specific functionality + * 3rd party clients of this API should not depend on future support or availability of this routine + */ + +/* DNSServiceSetDefaultDomainForUser() + * + * Set the default domain for the caller's UID. Future browse and registration + * calls by this user that do not specify an explicit domain will browse and + * register in this wide-area domain in addition to .local. In addition, this + * domain will be returned as a Browse domain via domain enumeration calls. + * + * + * Parameters: + * + * flags: Pass kDNSServiceFlagsAdd to add a domain for a user. Call without + * this flag set to clear a previously added domain. + * + * domain: The domain to be used for the caller's UID. + * + * return value: Returns kDNSServiceErr_NoError on succeses, otherwise returns + * an error code indicating the error that occurred + */ + +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser + ( + DNSServiceFlags flags, + const char *domain + ); + +#endif //__APPLE_API_PRIVATE + +// Some C compiler cleverness. We can make the compiler check certain things for us, +// and report errors at compile-time if anything is wrong. The usual way to do this would +// be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but +// then you don't find out what's wrong until you run the software. This way, if the assertion +// condition is false, the array size is negative, and the complier complains immediately. + +struct DNS_SD_CompileTimeAssertionChecks + { + char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1]; + }; + +#ifdef __cplusplus + } +#endif + +#endif /* _DNS_SD_H */ diff --git a/usr/src/lib/libdns_sd/common/dnssd_clientlib.c b/usr/src/lib/libdns_sd/common/dnssd_clientlib.c new file mode 100644 index 0000000000..b5a45c8b63 --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dnssd_clientlib.c @@ -0,0 +1,373 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): + +$Log: dnssd_clientlib.c,v $ +Revision 1.11 2006/08/14 23:05:53 cheshire +Added "tab-width" emacs header line + +Revision 1.10 2005/04/06 02:06:56 shersche +Add DNSSD_API macro to TXTRecord API calls + +Revision 1.9 2004/10/06 02:22:19 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.8 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.7 2004/06/26 03:16:34 shersche +clean up warning messages on Win32 platform + +Submitted by: herscher + +Revision 1.6 2004/06/12 01:09:45 cheshire +To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.) +API routines have to be declared as "__stdcall", instead of the C default, "__cdecl" + +Revision 1.5 2004/05/25 18:29:33 cheshire +Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, +so that it's also accessible to dnssd_clientshim.c (single address space) clients. + +Revision 1.4 2004/05/25 17:08:55 cheshire +Fix compiler warning (doesn't make sense for function return type to be const) + +Revision 1.3 2004/05/21 21:41:35 cheshire +Add TXT record building and parsing APIs + +Revision 1.2 2004/05/20 22:22:21 cheshire +Enable code that was bracketed by "#if 0" + +Revision 1.1 2004/03/12 21:30:29 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <string.h> + +#include "dns_sd.h" + +#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY +#pragma export on +#endif + +#if defined(_WIN32) +// disable warning "conversion from <data> to uint16_t" +#pragma warning(disable:4244) +#endif + +/********************************************************************************************* + * + * Supporting Functions + * + *********************************************************************************************/ + +#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') + +static int DomainEndsInDot(const char *dom) + { + while (dom[0] && dom[1]) + { + if (dom[0] == '\\') // advance past escaped byte sequence + { + if (mdnsIsDigit(dom[1]) && mdnsIsDigit(dom[2]) && mdnsIsDigit(dom[3])) + dom += 4; // If "\ddd" then skip four + else dom += 2; // else if "\x" then skip two + } + else dom++; // else goto next character + } + return (dom[0] == '.'); + } + +static uint8_t *InternalTXTRecordSearch + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + unsigned long *keylen + ) + { + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + *keylen = (unsigned long) strlen(key); + while (p<e) + { + uint8_t *x = p; + p += 1 + p[0]; + if (p <= e && *keylen <= x[0] && !strncmp(key, (char*)x+1, *keylen)) + if (*keylen == x[0] || x[1+*keylen] == '=') return(x); + } + return(NULL); + } + +/********************************************************************************************* + * + * General Utility Functions + * + *********************************************************************************************/ + +int DNSSD_API DNSServiceConstructFullName + ( + char *fullName, + const char *service, /* may be NULL */ + const char *regtype, + const char *domain + ) + { + unsigned long len; + unsigned char c; + char *fn = fullName; + const char *s = service; + const char *r = regtype; + const char *d = domain; + + if (service) + { + while(*s) + { + c = (unsigned char)*s++; + if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals + else if (c <= ' ') // escape non-printable characters + { + *fn++ = '\\'; + *fn++ = (char) ('0' + (c / 100)); + *fn++ = (char) ('0' + (c / 10) % 10); + c = (unsigned char)('0' + (c % 10)); + } + *fn++ = (char)c; + } + *fn++ = '.'; + } + + if (!regtype) return -1; + len = (unsigned long) strlen(regtype); + if (DomainEndsInDot(regtype)) len--; + if (len < 6) return -1; // regtype must be at least "x._udp" or "x._tcp" + if (strncmp((regtype + len - 4), "_tcp", 4) && strncmp((regtype + len - 4), "_udp", 4)) return -1; + while(*r) *fn++ = *r++; + if (!DomainEndsInDot(regtype)) *fn++ = '.'; + + if (!domain || !domain[0]) return -1; + while(*d) *fn++ = *d++; + if (!DomainEndsInDot(domain)) *fn++ = '.'; + *fn = '\0'; + return 0; + } + +/********************************************************************************************* + * + * TXT Record Construction Functions + * + *********************************************************************************************/ + +typedef struct _TXTRecordRefRealType + { + uint8_t *buffer; // Pointer to data + uint16_t buflen; // Length of buffer + uint16_t datalen; // Length currently in use + uint16_t malloced; // Non-zero if buffer was allocated via malloc() + } TXTRecordRefRealType; + +#define txtRec ((TXTRecordRefRealType*)txtRecord) + +// The opaque storage defined in the public dns_sd.h header is 16 bytes; +// make sure we don't exceed that. +struct dnssd_clientlib_CompileTimeAssertionCheck + { + char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1]; + }; + +void DNSSD_API TXTRecordCreate + ( + TXTRecordRef *txtRecord, + uint16_t bufferLen, + void *buffer + ) + { + txtRec->buffer = buffer; + txtRec->buflen = buffer ? bufferLen : (uint16_t)0; + txtRec->datalen = 0; + txtRec->malloced = 0; + } + +void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord) + { + if (txtRec->malloced) free(txtRec->buffer); + } + +DNSServiceErrorType DNSSD_API TXTRecordSetValue + ( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, + const void *value + ) + { + uint8_t *start, *p; + const char *k; + unsigned long keysize, keyvalsize; + + for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid); + keysize = (unsigned long)(k - key); + keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0); + if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid); + (void)TXTRecordRemoveValue(txtRecord, key); + if (txtRec->datalen + keyvalsize > txtRec->buflen) + { + unsigned char *newbuf; + unsigned long newlen = txtRec->datalen + keyvalsize; + if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid); + newbuf = malloc((size_t)newlen); + if (!newbuf) return(kDNSServiceErr_NoMemory); + memcpy(newbuf, txtRec->buffer, txtRec->datalen); + if (txtRec->malloced) free(txtRec->buffer); + txtRec->buffer = newbuf; + txtRec->buflen = (uint16_t)(newlen); + txtRec->malloced = 1; + } + start = txtRec->buffer + txtRec->datalen; + p = start + 1; + memcpy(p, key, keysize); + p += keysize; + if (value) + { + *p++ = '='; + memcpy(p, value, valueSize); + p += valueSize; + } + *start = (uint8_t)(p - start - 1); + txtRec->datalen += p - start; + return(kDNSServiceErr_NoError); + } + +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue + ( + TXTRecordRef *txtRecord, + const char *key + ) + { + unsigned long keylen, itemlen, remainder; + uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen); + if (!item) return(kDNSServiceErr_NoSuchKey); + itemlen = (unsigned long)(1 + item[0]); + remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen)); + // Use memmove because memcpy behaviour is undefined for overlapping regions + memmove(item, item + itemlen, remainder); + txtRec->datalen -= itemlen; + return(kDNSServiceErr_NoError); + } + +uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); } +const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); } + +/********************************************************************************************* + * + * TXT Record Parsing Functions + * + *********************************************************************************************/ + +int DNSSD_API TXTRecordContainsKey + ( + uint16_t txtLen, + const void *txtRecord, + const char *key + ) + { + unsigned long keylen; + return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0); + } + +const void * DNSSD_API TXTRecordGetValuePtr + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + uint8_t *valueLen + ) + { + unsigned long keylen; + uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen); + if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL + *valueLen = (uint8_t)(item[0] - (keylen + 1)); + return (item + 1 + keylen + 1); + } + +uint16_t DNSSD_API TXTRecordGetCount + ( + uint16_t txtLen, + const void *txtRecord + ) + { + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (p<e) { p += 1 + p[0]; count++; } + return((p>e) ? (uint16_t)0 : count); + } + +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex + ( + uint16_t txtLen, + const void *txtRecord, + uint16_t index, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value + ) + { + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (p<e && count<index) { p += 1 + p[0]; count++; } // Find requested item + if (p<e && p + 1 + p[0] <= e) // If valid + { + uint8_t *x = p+1; + unsigned long len = 0; + e = p + 1 + p[0]; + while (x+len<e && x[len] != '=') len++; + if (len >= keyBufLen) return(kDNSServiceErr_NoMemory); + memcpy(key, x, len); + key[len] = 0; + if (x+len<e) // If we found '=' + { + *value = x + len + 1; + *valueLen = (uint8_t)(p[0] - (len + 1)); + } + else + { + *value = NULL; + *valueLen = 0; + } + return(kDNSServiceErr_NoError); + } + return(kDNSServiceErr_Invalid); + } diff --git a/usr/src/lib/libdns_sd/common/dnssd_clientstub.c b/usr/src/lib/libdns_sd/common/dnssd_clientstub.c new file mode 100644 index 0000000000..2a22a0e22f --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dnssd_clientstub.c @@ -0,0 +1,1249 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): + +$Log: dnssd_clientstub.c,v $ +Revision 1.53 2006/09/07 04:43:12 herscher +Fix compile error on Win32 platform by moving inclusion of syslog.h + +Revision 1.52 2006/08/15 23:04:21 mkrochma +<rdar://problem/4090354> Client should be able to specify service name w/o callback + +Revision 1.51 2006/07/24 23:45:55 cheshire +<rdar://problem/4605276> DNSServiceReconfirmRecord() should return error code + +Revision 1.50 2006/06/28 08:22:27 cheshire +<rdar://problem/4605264> dnssd_clientstub.c needs to report unlink failures in syslog + +Revision 1.49 2006/06/28 07:58:59 cheshire +Minor textual tidying + +Revision 1.48 2005/06/30 18:01:00 shersche +<rdar://problem/4096913> Clients shouldn't wait ten seconds to connect to mDNSResponder + +Revision 1.47 2005/03/31 02:19:56 cheshire +<rdar://problem/4021486> Fix build warnings +Reviewed by: Scott Herscher + +Revision 1.46 2005/03/21 00:39:31 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.45 2005/02/01 01:25:06 shersche +Define sleep() to be Sleep() for Windows compatibility + +Revision 1.44 2005/01/27 22:57:56 cheshire +Fix compile errors on gcc4 + +Revision 1.43 2005/01/27 00:02:29 cheshire +<rdar://problem/3947461> Handle case where client runs before daemon has finished launching + +Revision 1.42 2005/01/11 02:01:02 shersche +Use dnssd_close() rather than close() for Windows compatibility + +Revision 1.41 2004/12/23 17:34:26 ksekar +<rdar://problem/3931319> Calls leak sockets if mDNSResponder is not running + +Revision 1.40 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.39 2004/11/12 03:22:00 rpantos +rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + +Revision 1.38 2004/11/02 02:51:23 cheshire +<rdar://problem/3526342> Remove overly-restrictive flag checks + +Revision 1.37 2004/10/14 01:43:35 cheshire +Fix opaque port passing problem + +Revision 1.36 2004/10/06 02:22:19 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.35 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.34 2004/09/17 22:36:13 cheshire +Add comment explaining that deliver_request frees the message it sends + +Revision 1.33 2004/09/17 01:17:31 ksekar +Remove double-free of msg header, freed automatically by deliver_request() + +Revision 1.32 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.31 2004/09/16 23:37:19 cheshire +Free hdr before returning + +Revision 1.30 2004/09/16 23:14:24 cheshire +Changes for Windows compatibility + +Revision 1.29 2004/09/16 21:46:38 ksekar +<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain + +Revision 1.28 2004/08/11 17:10:04 cheshire +Fix signed/unsigned warnings + +Revision 1.27 2004/08/11 00:54:16 cheshire +Change "hdr->op.request_op" to just "hdr->op" + +Revision 1.26 2004/07/26 06:07:27 shersche +fix bugs when using an error socket to communicate with the daemon + +Revision 1.25 2004/07/26 05:54:02 shersche +DNSServiceProcessResult() returns NoError if socket read returns EWOULDBLOCK + +Revision 1.24 2004/07/20 06:46:21 shersche +<rdar://problem/3730123> fix endless loop in read_all() if recv returns 0 +Bug #: 3730123 + +Revision 1.23 2004/06/29 00:48:38 cheshire +Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; +use an explicit while() loop instead. + +Revision 1.22 2004/06/26 03:16:34 shersche +clean up warning messages on Win32 platform + +Submitted by: herscher + +Revision 1.21 2004/06/18 04:53:56 rpantos +Use platform layer for socket types. Introduce USE_TCP_LOOPBACK. Remove dependency on mDNSEmbeddedAPI.h. + +Revision 1.20 2004/06/12 00:50:22 cheshire +Changes for Windows compatibility + +Revision 1.19 2004/05/25 18:29:33 cheshire +Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, +so that it's also accessible to dnssd_clientshim.c (single address space) clients. + +Revision 1.18 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.17 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +<rdar://problem/3592068>: Remove flags with zero value +<rdar://problem/3479569>: Passing in NULL causes a crash. + +Revision 1.16 2004/03/12 22:00:37 cheshire +Added: #include <sys/socket.h> + +Revision 1.15 2004/01/20 18:36:29 ksekar +Propagated Libinfo fix for <rdar://problem/3483971>: SU: +DNSServiceUpdateRecord() doesn't allow you to update the TXT record +into TOT mDNSResponder. + +Revision 1.14 2004/01/19 22:39:17 cheshire +Don't use "MSG_WAITALL"; it makes send() return "Invalid argument" on Linux; +use an explicit while() loop instead. (In any case, this should only make a difference +with non-blocking sockets, which we don't use on the client side right now.) + +Revision 1.13 2004/01/19 21:46:52 cheshire +Fix compiler warning + +Revision 1.12 2003/12/23 20:46:47 ksekar +<rdar://problem/3497428>: sync dnssd files between libinfo & mDNSResponder + +Revision 1.11 2003/12/08 21:11:42 rpantos +Changes necessary to support mDNSResponder on Linux. + +Revision 1.10 2003/10/13 23:50:53 ksekar +Updated dns_sd clientstub files to bring copies in synch with +top-of-tree Libinfo: A memory leak in dnssd_clientstub.c is fixed, +and comments in dns_sd.h are improved. + +Revision 1.9 2003/08/15 21:30:39 cheshire +Bring up to date with LibInfo version + +Revision 1.8 2003/08/13 23:54:52 ksekar +Bringing dnssd_clientstub.c up to date with Libinfo, per radar 3376640 + +Revision 1.7 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <errno.h> +#include <stdlib.h> + +#include "dnssd_ipc.h" + +#if defined(_WIN32) + +#include <winsock2.h> +#include <windows.h> + +#define sockaddr_mdns sockaddr_in +#define AF_MDNS AF_INET + +// disable warning: "'type cast' : from data pointer 'void *' to function pointer" +#pragma warning(disable:4055) + +// disable warning: "nonstandard extension, function/data pointer conversion in expression" +#pragma warning(disable:4152) + +extern BOOL IsSystemServiceDisabled(); + +#define sleep(X) Sleep((X) * 1000) + +static int g_initWinsock = 0; + +#else + +#include <sys/time.h> +#include <sys/socket.h> +#include <syslog.h> + +#define sockaddr_mdns sockaddr_un +#define AF_MDNS AF_LOCAL + +#endif + +// <rdar://problem/4096913> Specifies how many times we'll try and connect to the +// server. + +#define DNSSD_CLIENT_MAXTRIES 4 + +#define CTL_PATH_PREFIX "/tmp/dnssd_clippath." +// error socket (if needed) is named "dnssd_clipath.[pid].xxx:n" where xxx are the +// last 3 digits of the time (in seconds) and n is the 6-digit microsecond time + +// general utility functions +typedef struct _DNSServiceRef_t + { + dnssd_sock_t sockfd; // connected socket between client and daemon + uint32_t op; // request_op_t or reply_op_t + process_reply_callback process_reply; + void *app_callback; + void *app_context; + uint32_t max_index; //largest assigned record index - 0 if no additl. recs registered + } _DNSServiceRef_t; + +typedef struct _DNSRecordRef_t + { + void *app_context; + DNSServiceRegisterRecordReply app_callback; + DNSRecordRef recref; + uint32_t record_index; // index is unique to the ServiceDiscoveryRef + DNSServiceRef sdr; + } _DNSRecordRef_t; + +// exported functions + +// write len bytes. return 0 on success, -1 on error +static int write_all(dnssd_sock_t sd, char *buf, int len) + { + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. + //if (send(sd, buf, len, MSG_WAITALL) != len) return -1; + while (len) + { + ssize_t num_written = send(sd, buf, len, 0); + if (num_written < 0 || num_written > len) return -1; + buf += num_written; + len -= num_written; + } + return 0; + } + +// read len bytes. return 0 on success, -1 on error +static int read_all(dnssd_sock_t sd, char *buf, int len) + { + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. + //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1; + while (len) + { + ssize_t num_read = recv(sd, buf, len, 0); + if ((num_read == -1) && (errno == EINTR)) + continue; + if ((num_read < 0) || (num_read > len)) return -1; + // Return error -2 when no data received and errno is not set + if (num_read == 0) return -2; + buf += num_read; + len -= num_read; + } + return 0; + } + +/* create_hdr + * + * allocate and initialize an ipc message header. value of len should initially be the + * length of the data, and is set to the value of the data plus the header. data_start + * is set to point to the beginning of the data section. reuse_socket should be non-zero + * for calls that can receive an immediate error return value on their primary socket. + * if zero, the path to a control socket is appended at the beginning of the message buffer. + * data_start is set past this string. + */ + +static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int reuse_socket) + { + char *msg = NULL; + ipc_msg_hdr *hdr; + int datalen; +#if !defined(USE_TCP_LOOPBACK) + char ctrl_path[256]; +#endif + + if (!reuse_socket) + { +#if defined(USE_TCP_LOOPBACK) + *len += 2; // Allocate space for two-byte port number +#else + struct timeval time; + if (gettimeofday(&time, NULL) < 0) return NULL; + sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), + (unsigned long)(time.tv_sec & 0xFFF), (unsigned long)(time.tv_usec)); + *len += strlen(ctrl_path) + 1; +#endif + } + + datalen = (int) *len; + *len += sizeof(ipc_msg_hdr); + + // write message to buffer + msg = malloc(*len); + if (!msg) return NULL; + + bzero(msg, *len); + hdr = (void *)msg; + hdr->datalen = datalen; + hdr->version = VERSION; + hdr->op = op; + if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET; + *data_start = msg + sizeof(ipc_msg_hdr); +#if defined(USE_TCP_LOOPBACK) + // Put dummy data in for the port, since we don't know what + // it is yet. The data will get filled in before we + // send the message. This happens in deliver_request(). + if (!reuse_socket) put_short(0, data_start); +#else + if (!reuse_socket) put_string(ctrl_path, data_start); +#endif + return hdr; + } + + // return a connected service ref (deallocate with DNSServiceRefDeallocate) +static DNSServiceRef connect_to_server(void) + { + dnssd_sockaddr_t saddr; + DNSServiceRef sdr; + int NumTries = 0; + +#if defined(_WIN32) + if (!g_initWinsock) + { + WSADATA wsaData; + DNSServiceErrorType err; + + g_initWinsock = 1; + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsaData ); + + if (err != 0) return NULL; + } + + // <rdar://problem/4096913> If the system service is disabled, we only want to try + // to connect once + + if ( IsSystemServiceDisabled() ) + { + NumTries = DNSSD_CLIENT_MAXTRIES; + } + +#endif + + sdr = malloc(sizeof(_DNSServiceRef_t)); + if (!sdr) return(NULL); + sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (sdr->sockfd == dnssd_InvalidSocket) { free(sdr); return NULL; } +#if defined(USE_TCP_LOOPBACK) + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + saddr.sin_port = htons(MDNS_TCP_SERVERPORT); +#else + saddr.sun_family = AF_LOCAL; + strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH); +#endif + while (1) + { + int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); + if (!err) break; // If we succeeded, return sdr + // If we failed, then it may be because the daemon is still launching. + // This can happen for processes that launch early in the boot process, while the + // daemon is still coming up. Rather than fail here, we'll wait a bit and try again. + // If, after four seconds, we still can't connect to the daemon, + // then we give up and return a failure code. + if (++NumTries < DNSSD_CLIENT_MAXTRIES) + sleep(1); // Sleep a bit, then try again + else + { + dnssd_close(sdr->sockfd); + sdr->sockfd = dnssd_InvalidSocket; + free(sdr); + return NULL; + } + } + return sdr; + } + +static DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd) + { + ipc_msg_hdr *hdr = msg; + uint32_t datalen = hdr->datalen; + dnssd_sockaddr_t caddr, daddr; // (client and daemon address structs) + char *const data = (char *)msg + sizeof(ipc_msg_hdr); + dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket; + int ret; + dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr); + DNSServiceErrorType err = kDNSServiceErr_Unknown; + + if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown; + + if (!reuse_sd) + { + // setup temporary error socket + if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) < 0) + goto cleanup; + bzero(&caddr, sizeof(caddr)); + +#if defined(USE_TCP_LOOPBACK) + { + union { uint16_t s; u_char b[2]; } port; + caddr.sin_family = AF_INET; + caddr.sin_port = 0; + caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + ret = bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)); + if (ret < 0) goto cleanup; + if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) goto cleanup; + listen(listenfd, 1); + port.s = caddr.sin_port; + data[0] = port.b[0]; // don't switch the byte order, as the + data[1] = port.b[1]; // daemon expects it in network byte order + } +#else + { + mode_t mask = umask(0); + caddr.sun_family = AF_LOCAL; +// According to Stevens (section 3.2), there is no portable way to +// determine whether sa_len is defined on a particular platform. +#ifndef NOT_HAVE_SA_LEN + caddr.sun_len = sizeof(struct sockaddr_un); +#endif + //syslog(LOG_WARNING, "deliver_request: creating UDS: %s\n", data); + strcpy(caddr.sun_path, data); + ret = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)); + umask(mask); + if (ret < 0) goto cleanup; + listen(listenfd, 1); + } +#endif + } + + ConvertHeaderBytes(hdr); + //syslog(LOG_WARNING, "deliver_request writing %ld bytes\n", datalen + sizeof(ipc_msg_hdr)); + //syslog(LOG_WARNING, "deliver_request name is %s\n", (char *)msg + sizeof(ipc_msg_hdr)); + if (write_all(sdr->sockfd, msg, datalen + sizeof(ipc_msg_hdr)) < 0) + goto cleanup; + free(msg); + msg = NULL; + + if (reuse_sd) errsd = sdr->sockfd; + else + { + //syslog(LOG_WARNING, "deliver_request: accept\n"); + len = sizeof(daddr); + errsd = accept(listenfd, (struct sockaddr *)&daddr, &len); + //syslog(LOG_WARNING, "deliver_request: accept returned %d\n", errsd); + if (errsd < 0) goto cleanup; + } + + if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0) + err = kDNSServiceErr_Unknown; + else + err = ntohl(err); + + //syslog(LOG_WARNING, "deliver_request: retrieved error code %d\n", err); + +cleanup: + if (!reuse_sd) + { + if (listenfd > 0) dnssd_close(listenfd); + if (errsd > 0) dnssd_close(errsd); +#if !defined(USE_TCP_LOOPBACK) + // syslog(LOG_WARNING, "deliver_request: removing UDS: %s\n", data); + if (unlink(data) != 0) + syslog(LOG_WARNING, "WARNING: unlink(\"%s\") failed errno %d (%s)", data, errno, strerror(errno)); + // else syslog(LOG_WARNING, "deliver_request: removed UDS: %s\n", data); +#endif + } + if (msg) free(msg); + return err; + } + +int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) + { + if (!sdRef) return -1; + return (int) sdRef->sockfd; + } + +// handle reply from server, calling application client callback. If there is no reply +// from the daemon on the socket contained in sdRef, the call will block. +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) + { + ipc_msg_hdr hdr; + char *data; + int rderr; + + if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply) + return kDNSServiceErr_BadReference; + + rderr = read_all(sdRef->sockfd, (void *)&hdr, sizeof(hdr)); + if (rderr < 0) { + // return NoError on EWOULDBLOCK. This will handle the case + // where a non-blocking socket is told there is data, but + // it was a false positive. Can check errno when error + // code returned is -1 + if ((rderr == -1) && (dnssd_errno() == dnssd_EWOULDBLOCK)) + return kDNSServiceErr_NoError; + return kDNSServiceErr_Unknown; + } + ConvertHeaderBytes(&hdr); + if (hdr.version != VERSION) + return kDNSServiceErr_Incompatible; + data = malloc(hdr.datalen); + if (!data) return kDNSServiceErr_NoMemory; + if (read_all(sdRef->sockfd, data, hdr.datalen) < 0) + return kDNSServiceErr_Unknown; + sdRef->process_reply(sdRef, &hdr, data); + free(data); + return kDNSServiceErr_NoError; + } + +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef) + { + if (!sdRef) return; + if (sdRef->sockfd > 0) dnssd_close(sdRef->sockfd); + free(sdRef); + } + +static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + char fullname[kDNSServiceMaxDomainName]; + char target[kDNSServiceMaxDomainName]; + uint16_t txtlen; + union { uint16_t s; u_char b[2]; } port; + uint32_t ifi; + DNSServiceErrorType err; + unsigned char *txtrecord; + int str_error = 0; + (void)hdr; //unused + + flags = get_flags(&data); + ifi = get_long(&data); + err = get_error_code(&data); + if (get_string(&data, fullname, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, target, kDNSServiceMaxDomainName) < 0) str_error = 1; + port.b[0] = *data++; + port.b[1] = *data++; + txtlen = get_short(&data); + txtrecord = (unsigned char *)get_rdata(&data, txtlen); + + if (!err && str_error) err = kDNSServiceErr_Unknown; + ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port.s, txtlen, txtrecord, sdr->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceResolve + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; + + // calculate total message length + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += strlen(name) + 1; + len += strlen(regtype) + 1; + len += strlen(domain) + 1; + + hdr = create_hdr(resolve_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + sdr->op = resolve_request; + sdr->process_reply = handle_resolve_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex, ttl; + DNSServiceErrorType errorCode; + char name[kDNSServiceMaxDomainName]; + uint16_t rrtype, rrclass, rdlen; + char *rdata; + int str_error = 0; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + if (get_string(&data, name, kDNSServiceMaxDomainName) < 0) str_error = 1; + rrtype = get_short(&data); + rrclass = get_short(&data); + rdlen = get_short(&data); + rdata = get_rdata(&data, rdlen); + ttl = get_long(&data); + + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; + ((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass, + rdlen, rdata, ttl, sdr->app_context); + return; + } + +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!name) name = "\0"; + + // calculate total message length + len = sizeof(flags); + len += sizeof(uint32_t); //interfaceIndex + len += strlen(name) + 1; + len += 2 * sizeof(uint16_t); // rrtype, rrclass + + hdr = create_hdr(query_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = query_request; + sdr->process_reply = handle_query_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + char replyName[256], replyType[kDNSServiceMaxDomainName], + replyDomain[kDNSServiceMaxDomainName]; + int str_error = 0; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + if (get_string(&data, replyName, 256) < 0) str_error = 1; + if (get_string(&data, replyType, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, replyDomain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; + ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceBrowse + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, + DNSServiceBrowseReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!domain) domain = ""; + + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += strlen(regtype) + 1; + len += strlen(domain) + 1; + + hdr = create_hdr(browse_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + sdr->op = browse_request; + sdr->process_reply = handle_browse_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser + ( + DNSServiceFlags flags, + const char *domain + ) + { + DNSServiceRef sdr; + DNSServiceErrorType err; + char *ptr = NULL; + size_t len = sizeof(flags) + strlen(domain) + 1; + ipc_msg_hdr *hdr = create_hdr(setdomain_request, &len, &ptr, 1); + + if (!hdr) return kDNSServiceErr_Unknown; + put_flags(flags, &ptr); + put_string(domain, &ptr); + + sdr = connect_to_server(); + if (!sdr) { free(hdr); return kDNSServiceErr_Unknown; } + err = deliver_request((char *)hdr, sdr, 1); // deliver_request frees the message for us + DNSServiceRefDeallocate(sdr); + return err; + } + + +static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName]; + int str_error = 0; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + if (get_string(&data, name, 256) < 0) str_error = 1; + if (get_string(&data, regtype, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; + ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceRegister + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + const char *host, + uint16_t PortInNetworkByteOrder, + uint16_t txtLen, + const void *txtRecord, + DNSServiceRegisterReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder }; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!name) name = ""; + if (!regtype) return kDNSServiceErr_BadParam; + if (!domain) domain = ""; + if (!host) host = ""; + if (!txtRecord) txtRecord = (void*)""; + + // auto-name must also have auto-rename + if (!name[0] && (flags & kDNSServiceFlagsNoAutoRename)) + return kDNSServiceErr_BadParam; + + // no callback must have auto-rename + if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); // interfaceIndex + len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4; + len += 2 * sizeof(uint16_t); // port, txtLen + len += txtLen; + + hdr = create_hdr(reg_service_request, &len, &ptr, 1); + if (!hdr) goto error; + if (!callBack) hdr->flags |= IPC_FLAGS_NOREPLY; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + put_string(host, &ptr); + *ptr++ = port.b[0]; + *ptr++ = port.b[1]; + put_short(txtLen, &ptr); + put_rdata(txtLen, txtRecord, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = reg_service_request; + sdr->process_reply = callBack ? handle_regservice_response : NULL; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType err; + char domain[kDNSServiceMaxDomainName]; + int str_error = 0; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + err = get_error_code(&data); + if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!err && str_error) err = kDNSServiceErr_Unknown; + ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + int f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0; + int f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0; + if (f1 + f2 != 1) return kDNSServiceErr_BadParam; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + + hdr = create_hdr(enumeration_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = enumeration_request; + sdr->process_reply = handle_enumeration_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + DNSRecordRef rref = hdr->client_context.context; + + if (sdr->op != connection) + { + rref->app_callback(rref->sdr, rref, 0, kDNSServiceErr_Unknown, rref->app_context); + return; + } + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + + rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) + { + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = connect_to_server(); + if (!*sdRef) + return kDNSServiceErr_Unknown; + (*sdRef)->op = connection; + (*sdRef)->process_reply = handle_regrecord_response; + return 0; + } + +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr = NULL; + DNSServiceRef tmp = NULL; + DNSRecordRef rref = NULL; + int f1 = (flags & kDNSServiceFlagsShared) != 0; + int f2 = (flags & kDNSServiceFlagsUnique) != 0; + if (f1 + f2 != 1) return kDNSServiceErr_BadParam; + + if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0) + return kDNSServiceErr_BadReference; + *RecordRef = NULL; + + len = sizeof(DNSServiceFlags); + len += 2 * sizeof(uint32_t); // interfaceIndex, ttl + len += 3 * sizeof(uint16_t); // rrtype, rrclass, rdlen + len += strlen(fullname) + 1; + len += rdlen; + + hdr = create_hdr(reg_record_request, &len, &ptr, 0); + if (!hdr) goto error; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(fullname, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + + rref = malloc(sizeof(_DNSRecordRef_t)); + if (!rref) goto error; + rref->app_context = context; + rref->app_callback = callBack; + rref->record_index = sdRef->max_index++; + rref->sdr = sdRef; + *RecordRef = rref; + hdr->client_context.context = rref; + hdr->reg_index = rref->record_index; + + return deliver_request(msg, sdRef, 0); + +error: + if (rref) free(rref); + if (tmp) free(tmp); + if (hdr) free(hdr); + return kDNSServiceErr_Unknown; + } + +//sdRef returned by DNSServiceRegister() +DNSServiceErrorType DNSSD_API DNSServiceAddRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ) + { + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + DNSRecordRef rref; + + if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef) + return kDNSServiceErr_BadReference; + *RecordRef = NULL; + + len += 2 * sizeof(uint16_t); //rrtype, rdlen + len += rdlen; + len += sizeof(uint32_t); + len += sizeof(DNSServiceFlags); + + hdr = create_hdr(add_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + put_flags(flags, &ptr); + put_short(rrtype, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + + rref = malloc(sizeof(_DNSRecordRef_t)); + if (!rref) goto error; + rref->app_context = NULL; + rref->app_callback = NULL; + rref->record_index = sdRef->max_index++; + rref->sdr = sdRef; + *RecordRef = rref; + hdr->client_context.context = rref; + hdr->reg_index = rref->record_index; + return deliver_request((char *)hdr, sdRef, 0); + +error: + if (hdr) free(hdr); + if (rref) free(rref); + if (*RecordRef) *RecordRef = NULL; + return kDNSServiceErr_Unknown; +} + +//DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ) + { + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + + if (!sdRef) return kDNSServiceErr_BadReference; + + len += sizeof(uint16_t); + len += rdlen; + len += sizeof(uint32_t); + len += sizeof(DNSServiceFlags); + + hdr = create_hdr(update_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX; + put_flags(flags, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + return deliver_request((char *)hdr, sdRef, 0); + } + +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags + ) + { + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + DNSServiceErrorType err; + + if (!sdRef || !RecordRef || !sdRef->max_index) + return kDNSServiceErr_BadReference; + + len += sizeof(flags); + hdr = create_hdr(remove_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + hdr->reg_index = RecordRef->record_index; + put_flags(flags, &ptr); + err = deliver_request((char *)hdr, sdRef, 0); + if (!err) free(RecordRef); + return err; + } + +DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord + ( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata + ) + { + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef tmp; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + len += strlen(fullname) + 1; + len += 3 * sizeof(uint16_t); + len += rdlen; + tmp = connect_to_server(); + if (!tmp) return(kDNSServiceErr_Unknown); + hdr = create_hdr(reconfirm_record_request, &len, &ptr, 1); + if (!hdr) return(kDNSServiceErr_Unknown); + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(fullname, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + ConvertHeaderBytes(hdr); + write_all(tmp->sockfd, (char *)hdr, (int) len); + free(hdr); + DNSServiceRefDeallocate(tmp); + return(kDNSServiceErr_NoError); + } + diff --git a/usr/src/lib/libdns_sd/common/dnssd_ipc.c b/usr/src/lib/libdns_sd/common/dnssd_ipc.c new file mode 100644 index 0000000000..92e76bab0c --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dnssd_ipc.c @@ -0,0 +1,135 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): + +$Log: dnssd_ipc.c,v $ +Revision 1.16 2006/08/14 23:05:53 cheshire +Added "tab-width" emacs header line + +Revision 1.15 2005/01/27 22:57:56 cheshire +Fix compile errors on gcc4 + +Revision 1.14 2004/10/06 02:22:20 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.13 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.12 2004/09/16 23:14:24 cheshire +Changes for Windows compatibility + +Revision 1.11 2004/06/18 04:56:09 rpantos +casting goodness + +Revision 1.10 2004/06/12 01:08:14 cheshire +Changes for Windows compatibility + +Revision 1.9 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.8 2003/11/05 22:44:57 ksekar +<rdar://problem/3335230>: No bounds checking when reading data from client +Reviewed by: Stuart Cheshire + +Revision 1.7 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "dnssd_ipc.h" + +void put_long(const uint32_t l, char **ptr) + { + (*ptr)[0] = (char)((l >> 24) & 0xFF); + (*ptr)[1] = (char)((l >> 16) & 0xFF); + (*ptr)[2] = (char)((l >> 8) & 0xFF); + (*ptr)[3] = (char)((l ) & 0xFF); + *ptr += sizeof(uint32_t); + } + +uint32_t get_long(char **ptr) + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint32_t); + return((uint32_t) ((uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3])); + } + +void put_short(uint16_t s, char **ptr) + { + (*ptr)[0] = (char)((s >> 8) & 0xFF); + (*ptr)[1] = (char)((s ) & 0xFF); + *ptr += sizeof(uint16_t); + } + +uint16_t get_short(char **ptr) + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint16_t); + return((uint16_t) ((uint16_t)p[0] << 8 | p[1])); + } + +int put_string(const char *str, char **ptr) + { + if (!str) str = ""; + strcpy(*ptr, str); + *ptr += strlen(str) + 1; + return 0; + } + +int get_string(char **ptr, char *buffer, int buflen) + { + int overrun = (int)strlen(*ptr) < buflen ? 0 : -1; + strncpy(buffer, *ptr, buflen - 1); + buffer[buflen - 1] = '\0'; + *ptr += strlen(buffer) + 1; + return overrun; + } + +void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr) + { + memcpy(*ptr, rdata, rdlen); + *ptr += rdlen; + } + +char *get_rdata(char **ptr, int rdlen) + { + char *rd = *ptr; + *ptr += rdlen; + return rd; + } + +void ConvertHeaderBytes(ipc_msg_hdr *hdr) + { + hdr->version = htonl(hdr->version); + hdr->datalen = htonl(hdr->datalen); + hdr->flags = htonl(hdr->flags); + hdr->op = htonl(hdr->op ); + hdr->reg_index = htonl(hdr->reg_index); + } diff --git a/usr/src/lib/libdns_sd/common/dnssd_ipc.h b/usr/src/lib/libdns_sd/common/dnssd_ipc.h new file mode 100644 index 0000000000..2f76591f9a --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dnssd_ipc.h @@ -0,0 +1,253 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): + +$Log: dnssd_ipc.h,v $ +Revision 1.23 2006/08/14 23:05:53 cheshire +Added "tab-width" emacs header line + +Revision 1.22 2006/06/28 08:56:26 cheshire +Added "_op" to the end of the operation code enum values, +to differentiate them from the routines with the same names + +Revision 1.21 2005/09/29 06:38:13 herscher +Remove #define MSG_WAITALL on Windows. We don't use this macro anymore, and it's presence causes warnings to be emitted when compiling against the latest Microsoft Platform SDK. + +Revision 1.20 2005/03/21 00:39:31 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.19 2005/02/02 02:25:22 cheshire +<rdar://problem/3980388> /var/run/mDNSResponder should be /var/run/mdnsd on Linux + +Revision 1.18 2005/01/27 22:57:56 cheshire +Fix compile errors on gcc4 + +Revision 1.17 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.16 2004/11/12 03:21:41 rpantos +rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + +Revision 1.15 2004/10/06 02:22:20 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.14 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.13 2004/09/16 23:14:25 cheshire +Changes for Windows compatibility + +Revision 1.12 2004/09/16 21:46:38 ksekar +<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain + +Revision 1.11 2004/08/10 06:24:56 cheshire +Use types with precisely defined sizes for 'op' and 'reg_index', for better +compatibility if the daemon and the client stub are built using different compilers + +Revision 1.10 2004/07/07 17:39:25 shersche +Change MDNS_SERVERPORT from 5533 to 5354. + +Revision 1.9 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.8 2004/06/18 04:56:51 rpantos +Add layer for platform code + +Revision 1.7 2004/06/12 01:08:14 cheshire +Changes for Windows compatibility + +Revision 1.6 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef DNSSD_IPC_H +#define DNSSD_IPC_H + +#include "dns_sd.h" + + +// +// Common cross platform services +// +#if defined(WIN32) +# include <winsock2.h> +# define dnssd_InvalidSocket INVALID_SOCKET +# define dnssd_EWOULDBLOCK WSAEWOULDBLOCK +# define dnssd_EINTR WSAEINTR +# define dnssd_sock_t SOCKET +# define dnssd_socklen_t int +# define dnssd_sockbuf_t const char* +# define dnssd_close(sock) closesocket(sock) +# define dnssd_errno() WSAGetLastError() +# define ssize_t int +# define getpid _getpid +#else +# include <sys/types.h> +# include <unistd.h> +# include <sys/un.h> +# include <string.h> +# include <stdio.h> +# include <stdlib.h> +# include <sys/stat.h> +# include <sys/socket.h> +# include <netinet/in.h> +# define dnssd_InvalidSocket -1 +# define dnssd_EWOULDBLOCK EWOULDBLOCK +# define dnssd_EINTR EINTR +# define dnssd_EPIPE EPIPE +# define dnssd_sock_t int +# define dnssd_socklen_t unsigned int +# define dnssd_sockbuf_t const char* +# define dnssd_close(sock) close(sock) +# define dnssd_errno() errno +#endif + +#if defined(USE_TCP_LOOPBACK) +# define AF_DNSSD AF_INET +# define MDNS_TCP_SERVERADDR "127.0.0.1" +# define MDNS_TCP_SERVERPORT 5354 +# define LISTENQ 5 +# define dnssd_sockaddr_t struct sockaddr_in +#else +# define AF_DNSSD AF_LOCAL +# ifndef MDNS_UDS_SERVERPATH +# define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder" +# endif +# define LISTENQ 100 + // longest legal control path length +# define MAX_CTLPATH 256 +# define dnssd_sockaddr_t struct sockaddr_un +#endif + + +//#define UDSDEBUG // verbose debug output + +// Compatibility workaround +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + +// General UDS constants +#define TXT_RECORD_INDEX ((uint32_t)(-1)) // record index for default text record + +// IPC data encoding constants and types +#define VERSION 1 +#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client +#define IPC_FLAGS_REUSE_SOCKET 2 // set flag if synchronous errors are to be sent via the primary socket + // (if not set, first string in message buffer must be path to error socket + +typedef enum + { + connection = 1, // connected socket via DNSServiceConnect() + reg_record_request, // reg/remove record only valid for connected sockets + remove_record_request, + enumeration_request, + reg_service_request, + browse_request, + resolve_request, + query_request, + reconfirm_record_request, + add_record_request, + update_record_request, + setdomain_request + } request_op_t; + +typedef enum + { + enumeration_reply_op = 64, + reg_service_reply_op, + browse_reply_op, + resolve_reply_op, + query_reply_op, + reg_record_reply_op + } reply_op_t; + +typedef struct ipc_msg_hdr_struct ipc_msg_hdr; + +// client stub callback to process message from server and deliver results to +// client application + +typedef void (*process_reply_callback) + ( + DNSServiceRef sdr, + ipc_msg_hdr *hdr, + char *msg + ); + +// allow 64-bit client to interoperate w/ 32-bit daemon +typedef union + { + void *context; + uint32_t ptr64[2]; + } client_context_t; + +typedef struct ipc_msg_hdr_struct + { + uint32_t version; + uint32_t datalen; + uint32_t flags; + uint32_t op; // request_op_t or reply_op_t + client_context_t client_context; // context passed from client, returned by server in corresponding reply + uint32_t reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a + // socket connected by DNSServiceConnect(). Must be unique in the scope of the connection, such that and + // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord()) + uint32_t padbytes; + } ipc_msg_hdr_struct; + +// it is advanced to point to the next field, or the end of the message +// routines to write to and extract data from message buffers. +// caller responsible for bounds checking. +// ptr is the address of the pointer to the start of the field. +// it is advanced to point to the next field, or the end of the message + +void put_long(const uint32_t l, char **ptr); +uint32_t get_long(char **ptr); + +void put_short(uint16_t s, char **ptr); +uint16_t get_short(char **ptr); + +#define put_flags put_long +#define get_flags get_long + +#define put_error_code put_long +#define get_error_code get_long + +int put_string(const char *str, char **ptr); +int get_string(char **ptr, char *buffer, int buflen); + +void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr); +char *get_rdata(char **ptr, int rdlen); // return value is rdata pointed to by *ptr - + // rdata is not copied from buffer. + +void ConvertHeaderBytes(ipc_msg_hdr *hdr); + +#endif // DNSSD_IPC_H diff --git a/usr/src/lib/libdns_sd/common/llib-ldns_sd b/usr/src/lib/libdns_sd/common/llib-ldns_sd new file mode 100644 index 0000000000..464e3b9a6d --- /dev/null +++ b/usr/src/lib/libdns_sd/common/llib-ldns_sd @@ -0,0 +1,32 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <dns_sd.h> diff --git a/usr/src/lib/libdns_sd/common/mapfile-vers b/usr/src/lib/libdns_sd/common/mapfile-vers new file mode 100644 index 0000000000..012b730ca1 --- /dev/null +++ b/usr/src/lib/libdns_sd/common/mapfile-vers @@ -0,0 +1,56 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +SUNW_1.1 { + global: + DNSServiceAddRecord; + DNSServiceBrowse; + DNSServiceConstructFullName; + DNSServiceCreateConnection; + DNSServiceEnumerateDomains; + DNSServiceProcessResult; + DNSServiceQueryRecord; + DNSServiceReconfirmRecord; + DNSServiceRefDeallocate; + DNSServiceRefSockFD; + DNSServiceRegister; + DNSServiceRegisterRecord; + DNSServiceRemoveRecord; + DNSServiceResolve; + DNSServiceUpdateRecord; + TXTRecordContainsKey; + TXTRecordCreate; + TXTRecordDeallocate; + TXTRecordGetBytesPtr; + TXTRecordGetCount; + TXTRecordGetItemAtIndex; + TXTRecordGetLength; + TXTRecordGetValuePtr; + TXTRecordRemoveValue; + TXTRecordSetValue; + local: + *; +}; diff --git a/usr/src/lib/libdns_sd/i386/Makefile b/usr/src/lib/libdns_sd/i386/Makefile new file mode 100644 index 0000000000..9c1be5bf3a --- /dev/null +++ b/usr/src/lib/libdns_sd/i386/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libdns_sd/java/Makefile b/usr/src/lib/libdns_sd/java/Makefile new file mode 100644 index 0000000000..5b015fc86e --- /dev/null +++ b/usr/src/lib/libdns_sd/java/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/lib/Makefile.lib + +POFILE = libjdns_sd.po + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all install clean clobber lint: com/apple .WAIT $(SUBDIRS) + +com/apple $(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/libdns_sd/java/Makefile.com b/usr/src/lib/libdns_sd/java/Makefile.com new file mode 100644 index 0000000000..f66463ac2f --- /dev/null +++ b/usr/src/lib/libdns_sd/java/Makefile.com @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +LIBRARY= libjdns_sd.a +VERS= .1 + +OBJECTS= JNISupport.o + +include $(SRC)/lib/Makefile.lib + +LIBS = $(DYNLIB) + +SRCDIR = ../common + +C99MODE = $(C99_ENABLE) +CPPFLAGS += -I$(JAVA_ROOT)/include -I$(JAVA_ROOT)/include/solaris +CPPFLAGS += -I../com/apple/dnssd +CPPFLAGS += -D_REENTRANT + +LDLIBS += -lc -lsocket -ldns_sd + +CLEANFILES= $(LINTOUT) $(LINTLIB) + +LINTLIB = + +.KEEP_STATE: + +lint: lintcheck + +include $(SRC)/lib/Makefile.targ + +pics/%.o: ../common/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/libdns_sd/java/amd64/Makefile b/usr/src/lib/libdns_sd/java/amd64/Makefile new file mode 100644 index 0000000000..b645e1caed --- /dev/null +++ b/usr/src/lib/libdns_sd/java/amd64/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../../Makefile.lib.64 + +all: $(LIBS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libdns_sd/java/com/apple/Makefile b/usr/src/lib/libdns_sd/java/com/apple/Makefile new file mode 100644 index 0000000000..8cfdc2e941 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include Makefile.com + +SUBDIRS = dnssd + +all:= TARGET = all +clean:= TARGET = clean +clobber:= TARGET = clobber +install:= TARGET = install + +.KEEP_STATE: + +all clean install clobber: ${SUBDIRS} + +${SUBDIRS}: FRC + cd $@; pwd; $(MAKE) $(TARGET) + +lint: + +.WAIT: + +FRC: diff --git a/usr/src/lib/libdns_sd/java/com/apple/Makefile.com b/usr/src/lib/libdns_sd/java/com/apple/Makefile.com new file mode 100644 index 0000000000..e699ff14a4 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/Makefile.com @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/lib/Makefile.lib + +ROOTDNSSDJAVAHOME = $(ROOT)/usr/share/lib/java + +.KEEP_STATE: diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java new file mode 100644 index 0000000000..689cb8ba57 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java @@ -0,0 +1,51 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: BaseListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A base class for DNSSD listeners. */ + +public interface BaseListener +{ + /** Called to report DNSSD operation failures.<P> + + @param service + The service that encountered the failure. + <P> + @param errorCode + Indicates the failure that occurred. See {@link DNSSDException} for error codes. + */ + void operationFailed( DNSSDService service, int errorCode); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java new file mode 100644 index 0000000000..0ae9b938ba --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java @@ -0,0 +1,88 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: BrowseListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSD#browse}. */ + +public interface BrowseListener extends BaseListener +{ + /** Called to report discovered services.<P> + + @param browser + The active browse service. + <P> + @param flags + Possible values are DNSSD.MORE_COMING. + <P> + @param ifIndex + The interface on which the service is advertised. This index should be passed + to {@link DNSSD#resolve} when resolving the service. + <P> + @param serviceName + The service name discovered. + <P> + @param regType + The registration type, as passed in to DNSSD.browse(). + <P> + @param domain + The domain in which the service was discovered. + */ + void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain); + + /** Called to report services which have been deregistered.<P> + + @param browser + The active browse service. + <P> + @param flags + Possible values are DNSSD.MORE_COMING. + <P> + @param ifIndex + The interface on which the service is advertised. + <P> + @param serviceName + The service name which has deregistered. + <P> + @param regType + The registration type, as passed in to DNSSD.browse(). + <P> + @param domain + The domain in which the service was discovered. + */ + void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java new file mode 100644 index 0000000000..6caf3a7791 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java @@ -0,0 +1,68 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSRecord.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/12/11 03:00:59 rpantos +<rdar://problem/3907498> Java DNSRecord API should be cleaned up + +Revision 1.1 2004/04/30 16:32:34 rpantos +First checked in. + + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** + Reference to a record returned by {@link DNSSDRegistration#addRecord}.<P> + + Note: client is responsible for serializing access to these objects if + they are shared between concurrent threads. +*/ + +public interface DNSRecord +{ + /** Update a registered resource record.<P> + The record must either be the primary txt record of a service registered via DNSSD.register(), + or a record added to a registered service via addRecord().<P> + + @param flags + Currently unused, reserved for future use. + <P> + @param rData + The new rdata to be contained in the updated resource record. + <P> + @param ttl + The time to live of the updated resource record, in seconds. + */ + void update( int flags, byte[] rData, int ttl) + throws DNSSDException; + + /** Remove a registered resource record.<P> + */ + void remove() + throws DNSSDException; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java new file mode 100644 index 0000000000..9841c9492b --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java @@ -0,0 +1,897 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSSD.java,v $ +Revision 1.11 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.10 2006/06/20 23:05:55 rpantos +<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent + +Revision 1.9 2005/10/26 01:52:24 cheshire +<rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux) + +Revision 1.8 2005/07/11 01:55:21 cheshire +<rdar://problem/4175511> Race condition in Java API + +Revision 1.7 2005/07/05 13:01:52 cheshire +<rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time + +Revision 1.6 2005/07/05 00:02:25 cheshire +Add missing comma + +Revision 1.5 2005/07/04 21:13:47 cheshire +Add missing error message strings + +Revision 1.4 2004/12/11 03:00:59 rpantos +<rdar://problem/3907498> Java DNSRecord API should be cleaned up + +Revision 1.3 2004/11/12 03:23:08 rpantos +rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex. + +Revision 1.2 2004/05/20 17:43:18 cheshire +Fix invalid UTF-8 characters in file + +Revision 1.1 2004/04/30 16:32:34 rpantos +First checked in. + + + This file declares and implements DNSSD, the central Java factory class + for doing DNS Service Discovery. It includes the mostly-abstract public + interface, as well as the Apple* implementation subclasses. + */ + +/* + * ident "%Z%%M% %I% %E% SMI" + */ + +package com.apple.dnssd; + + +/** + DNSSD provides access to DNS Service Discovery features of ZeroConf networking.<P> + + It is a factory class that is used to invoke registration and discovery-related + operations. Most operations are non-blocking; clients are called back through an interface + with the result of an operation. Callbacks are made from a separate worker thread.<P> + + For example, in this program<P> + <PRE><CODE> + class MyClient implements BrowseListener { + void lookForWebServers() { + myBrowser = DNSSD.browse("_http_.tcp", this); + } + + public void serviceFound(DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) {} + ... + }</CODE></PRE> + <CODE>MyClient.serviceFound()</CODE> would be called for every HTTP server discovered in the + default browse domain(s). +*/ + +abstract public class DNSSD +{ + /** Flag indicates to a {@link BrowseListener} that another result is + queued. Applications should not update their UI to display browse + results if the MORE_COMING flag is set; they will be called at least once + more with the flag clear. + */ + public static final int MORE_COMING = ( 1 << 0 ); + + /** If flag is set in a {@link DomainListener} callback, indicates that the result is the default domain. */ + public static final int DEFAULT = ( 1 << 2 ); + + /** If flag is set, a name conflict will trigger an exception when registering non-shared records.<P> + A name must be explicitly specified when registering a service if this bit is set + (i.e. the default name may not not be used). + */ + public static final int NO_AUTO_RENAME = ( 1 << 3 ); + + /** If flag is set, allow multiple records with this name on the network (e.g. PTR records) + when registering individual records on a {@link DNSSDRegistration}. + */ + public static final int SHARED = ( 1 << 4 ); + + /** If flag is set, records with this name must be unique on the network (e.g. SRV records). */ + public static final int UNIQUE = ( 1 << 5 ); + + /** Set flag when calling enumerateDomains() to restrict results to domains recommended for browsing. */ + public static final int BROWSE_DOMAINS = ( 1 << 6 ); + /** Set flag when calling enumerateDomains() to restrict results to domains recommended for registration. */ + public static final int REGISTRATION_DOMAINS = ( 1 << 7 ); + + /** Maximum length, in bytes, of a domain name represented as an escaped C-String. */ + public static final int MAX_DOMAIN_NAME = 1005; + + /** Pass for ifIndex to specify all available interfaces. */ + public static final int ALL_INTERFACES = 0; + + /** Pass for ifIndex to specify the localhost interface. */ + public static final int LOCALHOST_ONLY = -1; + + /** Browse for instances of a service.<P> + + Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P> + + @param flags + Currently ignored, reserved for future use. + <P> + @param ifIndex + If non-zero, specifies the interface on which to browse for services + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Most applications will pass 0 to browse on all available + interfaces. Pass -1 to only browse for services provided on the local host. + <P> + @param regType + The registration type being browsed for followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param domain + If non-null, specifies the domain on which to browse for services. + Most applications will not specify a domain, instead browsing on the + default domain(s). + <P> + @param listener + This object will get called when instances of the service are discovered (or disappear). + <P> + @return A {@link DNSSDService} that represents the active browse operation. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService browse( int flags, int ifIndex, String regType, String domain, BrowseListener listener) + throws DNSSDException + { return getInstance()._makeBrowser( flags, ifIndex, regType, domain, listener); } + + /** Browse for instances of a service. Use default flags, ifIndex and domain.<P> + + @param regType + The registration type being browsed for followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param listener + This object will get called when instances of the service are discovered (or disappear). + <P> + @return A {@link DNSSDService} that represents the active browse operation. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService browse( String regType, BrowseListener listener) + throws DNSSDException + { return browse( 0, 0, regType, "", listener); } + + /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.<P> + + Note: Applications should NOT use resolve() solely for txt record monitoring - use + queryRecord() instead, as it is more efficient for this task.<P> + + Note: When the desired results have been returned, the client MUST terminate the resolve by + calling {@link DNSSDService#stop}.<P> + + Note: resolve() behaves correctly for typical services that have a single SRV record and + a single TXT record (the TXT record may be empty.) To resolve non-standard services with + multiple SRV or TXT records, use queryRecord().<P> + + @param flags + Currently ignored, reserved for future use. + <P> + @param ifIndex + The interface on which to resolve the service. The client should + pass the interface on which the serviceName was discovered (i.e. + the ifIndex passed to the serviceFound() callback) + or 0 to resolve the named service on all available interfaces. + <P> + @param serviceName + The servicename to be resolved. + <P> + @param regType + The registration type being resolved followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param domain + The domain on which the service is registered, i.e. the domain passed + to the serviceFound() callback. + <P> + @param listener + This object will get called when the service is resolved. + <P> + @return A {@link DNSSDService} that represents the active resolve operation. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService resolve( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener listener) + throws DNSSDException + { return getInstance()._resolve( flags, ifIndex, serviceName, regType, domain, listener); } + + /** Register a service, to be discovered via browse() and resolve() calls.<P> + @param flags + Possible values are: NO_AUTO_RENAME. + <P> + @param ifIndex + If non-zero, specifies the interface on which to register the service + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Most applications will pass 0 to register on all + available interfaces. Pass -1 to register a service only on the local + machine (service will not be visible to remote hosts). + <P> + @param serviceName + If non-null, specifies the service name to be registered. + Applications need not specify a name, in which case the + computer name is used (this name is communicated to the client via + the serviceRegistered() callback). + <P> + @param regType + The registration type being registered followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param domain + If non-null, specifies the domain on which to advertise the service. + Most applications will not specify a domain, instead automatically + registering in the default domain(s). + <P> + @param host + If non-null, specifies the SRV target host name. Most applications + will not specify a host, instead automatically using the machine's + default host name(s). Note that specifying a non-null host does NOT + create an address record for that host - the application is responsible + for ensuring that the appropriate address record exists, or creating it + via {@link DNSSDRegistration#addRecord}. + <P> + @param port + The port on which the service accepts connections. Pass 0 for a + "placeholder" service (i.e. a service that will not be discovered by + browsing, but will cause a name conflict if another client tries to + register that same name.) Most clients will not use placeholder services. + <P> + @param txtRecord + The txt record rdata. May be null. Note that a non-null txtRecord + MUST be a properly formatted DNS TXT record, i.e. <length byte> <data> + <length byte> <data> ... + <P> + @param listener + This object will get called when the service is registered. + <P> + @return A {@link DNSSDRegistration} that controls the active registration. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDRegistration register( int flags, int ifIndex, String serviceName, String regType, + String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener) + throws DNSSDException + { return getInstance()._register( flags, ifIndex, serviceName, regType, domain, host, port, txtRecord, listener); } + + /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.<P> + @param serviceName + If non-null, specifies the service name to be registered. + Applications need not specify a name, in which case the + computer name is used (this name is communicated to the client via + the serviceRegistered() callback). + <P> + @param regType + The registration type being registered followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param port + The port on which the service accepts connections. Pass 0 for a + "placeholder" service (i.e. a service that will not be discovered by + browsing, but will cause a name conflict if another client tries to + register that same name.) Most clients will not use placeholder services. + <P> + @param listener + This object will get called when the service is registered. + <P> + @return A {@link DNSSDRegistration} that controls the active registration. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDRegistration register( String serviceName, String regType, int port, RegisterListener listener) + throws DNSSDException + { return register( 0, 0, serviceName, regType, null, null, port, null, listener); } + + /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of + multiple individual records.<P> + <P> + @return A {@link DNSSDRecordRegistrar} that can be used to register records. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDRecordRegistrar createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { return getInstance()._createRecordRegistrar( listener); } + + /** Query for an arbitrary DNS record.<P> + @param flags + Possible values are: MORE_COMING. + <P> + @param ifIndex + If non-zero, specifies the interface on which to issue the query + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Passing 0 causes the name to be queried for on all + interfaces. Passing -1 causes the name to be queried for only on the + local host. + <P> + @param serviceName + The full domain name of the resource record to be queried for. + <P> + @param rrtype + The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc) + as defined in nameser.h. + <P> + @param rrclass + The class of the resource record, as defined in nameser.h + (usually 1 for the Internet class). + <P> + @param listener + This object will get called when the query completes. + <P> + @return A {@link DNSSDService} that controls the active query. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener listener) + throws DNSSDException + { return getInstance()._queryRecord( flags, ifIndex, serviceName, rrtype, rrclass, listener); } + + /** Asynchronously enumerate domains available for browsing and registration.<P> + + Currently, the only domain returned is "local.", but other domains will be returned in future.<P> + + The enumeration MUST be cancelled by calling {@link DNSSDService#stop} when no more domains + are to be found.<P> + @param flags + Possible values are: BROWSE_DOMAINS, REGISTRATION_DOMAINS. + <P> + @param ifIndex + If non-zero, specifies the interface on which to look for domains. + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Most applications will pass 0 to enumerate domains on + all interfaces. + <P> + @param listener + This object will get called when domains are found. + <P> + @return A {@link DNSSDService} that controls the active enumeration. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService enumerateDomains( int flags, int ifIndex, DomainListener listener) + throws DNSSDException + { return getInstance()._enumerateDomains( flags, ifIndex, listener); } + + /** Concatenate a three-part domain name (as provided to the listeners) into a + properly-escaped full domain name. Note that strings passed to listeners are + ALREADY ESCAPED where necessary.<P> + @param serviceName + The service name - any dots or slashes must NOT be escaped. + May be null (to construct a PTR record name, e.g. "_ftp._tcp.apple.com"). + <P> + @param regType + The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). + <P> + @param domain + The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped. + <P> + @return The full domain name. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static String constructFullName( String serviceName, String regType, String domain) + throws DNSSDException + { return getInstance()._constructFullName( serviceName, regType, domain); } + + /** Instruct the daemon to verify the validity of a resource record that appears to + be out of date. (e.g. because tcp connection to a service's target failed.) <P> + + Causes the record to be flushed from the daemon's cache (as well as all other + daemons' caches on the network) if the record is determined to be invalid.<P> + @param flags + Currently unused, reserved for future use. + <P> + @param ifIndex + If non-zero, specifies the interface on which to reconfirm the record + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Passing 0 causes the name to be reconfirmed on all + interfaces. Passing -1 causes the name to be reconfirmed only on the + local host. + <P> + @param fullName + The resource record's full domain name. + <P> + @param rrtype + The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + <P> + @param rrclass + The class of the resource record, as defined in nameser.h (usually 1). + <P> + @param rdata + The raw rdata of the resource record. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static void reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + int rrclass, byte[] rdata) + { getInstance()._reconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); } + + /** Return the canonical name of a particular interface index.<P> + @param ifIndex + A valid interface index. Must not be ALL_INTERFACES. + <P> + @return The name of the interface, which should match java.net.NetworkInterface.getName(). + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static String getNameForIfIndex( int ifIndex) + { return getInstance()._getNameForIfIndex( ifIndex); } + + /** Return the index of a named interface.<P> + @param ifName + A valid interface name. An example is java.net.NetworkInterface.getName(). + <P> + @return The interface index. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static int getIfIndexForName( String ifName) + { return getInstance()._getIfIndexForName( ifName); } + + protected DNSSD() {} // prevent direct instantiation + + /** Return the single instance of DNSSD. */ + static protected final DNSSD getInstance() + { + SecurityManager sm = System.getSecurityManager(); + if ( sm != null) + sm.checkPermission( new RuntimePermission( "getDNSSDInstance")); + return fInstance; + } + + abstract protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener listener) + throws DNSSDException; + + abstract protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener listener) + throws DNSSDException; + + abstract protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType, + String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener) + throws DNSSDException; + + abstract protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException; + + abstract protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener listener) + throws DNSSDException; + + abstract protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener) + throws DNSSDException; + + abstract protected String _constructFullName( String serviceName, String regType, String domain) + throws DNSSDException; + + abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + int rrclass, byte[] rdata); + + abstract protected String _getNameForIfIndex( int ifIndex); + + abstract protected int _getIfIndexForName( String ifName); + + protected static DNSSD fInstance; + + static + { + try + { + String name = System.getProperty( "com.apple.dnssd.DNSSD" ); + if( name == null ) + name = "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class. + fInstance = (DNSSD) Class.forName( name ).newInstance(); + } + catch( Exception e ) + { + throw new InternalError( "cannot instantiate DNSSD" + e ); + } + } +} + + +// Concrete implementation of DNSSDException +class AppleDNSSDException extends DNSSDException +{ + public AppleDNSSDException( int errorCode) { fErrorCode = errorCode; } + + public int getErrorCode() { return fErrorCode; } + + public String getMessage() + { + final String kMessages[] = { // should probably be put into a resource or something + "UNKNOWN", + "NO_SUCH_NAME", + "NO_MEMORY", + "BAD_PARAM", + "BAD_REFERENCE", + "BAD_STATE", + "BAD_FLAGS", + "UNSUPPORTED", + "NOT_INITIALIZED", + "NO_CACHE", + "ALREADY_REGISTERED", + "NAME_CONFLICT", + "INVALID", + "FIREWALL", + "INCOMPATIBLE", + "BAD_INTERFACE_INDEX", + "REFUSED", + "NOSUCHRECORD", + "NOAUTH", + "NOSUCHKEY", + "NATTRAVERSAL", + "DOUBLENAT", + "BADTIME", + "BADSIG", + "BADKEY", + "TRANSIENT" + }; + + if ( fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length)) + { + return "DNS-SD Error " + String.valueOf( fErrorCode) + ": " + kMessages[ UNKNOWN - fErrorCode]; + } + else + return super.getMessage() + "(" + String.valueOf( fErrorCode) + ")"; + } + + protected int fErrorCode; +} + +// The concrete, default implementation. +class AppleDNSSD extends DNSSD +{ + static + { + System.loadLibrary( "jdns_sd"); + + int libInitResult = InitLibrary( 1); + + if ( libInitResult != DNSSDException.NO_ERROR) + throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult).getMessage()); + } + + static public boolean hasAutoCallbacks; // Set by InitLibrary() to value of AUTO_CALLBACKS + + protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client) + throws DNSSDException + { + return new AppleBrowser( flags, ifIndex, regType, domain, client); + } + + protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener client) + throws DNSSDException + { + return new AppleResolver( flags, ifIndex, serviceName, regType, domain, client); + } + + protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType, + String domain, String host, int port, TXTRecord txtRecord, RegisterListener client) + throws DNSSDException + { + return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port, + ( txtRecord != null) ? txtRecord.getRawBytes() : null, client); + } + + protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { + return new AppleRecordRegistrar( listener); + } + + protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener client) + throws DNSSDException + { + return new AppleQuery( flags, ifIndex, serviceName, rrtype, rrclass, client); + } + + protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener) + throws DNSSDException + { + return new AppleDomainEnum( flags, ifIndex, listener); + } + + protected String _constructFullName( String serviceName, String regType, String domain) + throws DNSSDException + { + String[] responseHolder = new String[1]; // lame maneuver to get around Java's lack of reference parameters + + int rc = ConstructName( serviceName, regType, domain, responseHolder); + if ( rc != 0) + throw new AppleDNSSDException( rc); + + return responseHolder[0]; + } + + protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + int rrclass, byte[] rdata) + { + ReconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); + } + + protected String _getNameForIfIndex( int ifIndex) + { + return GetNameForIfIndex( ifIndex); + } + + protected int _getIfIndexForName( String ifName) + { + return GetIfIndexForName( ifName); + } + + + protected native int ConstructName( String serviceName, String regType, String domain, String[] pOut); + + protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + int rrclass, byte[] rdata); + + protected native String GetNameForIfIndex( int ifIndex); + + protected native int GetIfIndexForName( String ifName); + + protected static native int InitLibrary( int callerVersion); +} + +class AppleService implements DNSSDService, Runnable +{ + public AppleService(BaseListener listener) { fNativeContext = 0; fListener = listener; } + + public void stop() { this.HaltOperation(); } + + /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */ + protected native int BlockForData(); + + /* Call ProcessResults when data appears on socket descriptor. */ + protected native int ProcessResults(); + + protected synchronized native void HaltOperation(); + + protected void ThrowOnErr( int rc) throws DNSSDException + { + if ( rc != 0) + throw new AppleDNSSDException( rc); + } + + protected long /* warning */ fNativeContext; // Private storage for native side + + public void run() + { + while ( true ) + { + // Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to + // block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized + // we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread, + // and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD + // operation. The Unix kernel always allocates the lowest available file descriptor to a new socket, + // so the same file descriptor is highly likely to be reused for the new operation, and if our old + // stale ServiceThread accidentally consumes bytes off that new socket we'll get really messed up. + // To guard against that, before calling ProcessResults we check to ensure that our + // fNativeContext has not been deleted, which is a telltale sign that our operation was stopped. + // After calling ProcessResults we check again, because it's extremely common for callback + // functions to stop their own operation and start others. For example, a resolveListener callback + // may well stop the resolve and then start a QueryRecord call to monitor the TXT record. + // + // The remaining risk is that between our checking fNativeContext and calling ProcessResults(), + // some other thread could stop the operation and start a new one using same file descriptor, and + // we wouldn't know. To prevent this, the AppleService object's HaltOperation() routine is declared + // synchronized and we perform our checks synchronized on the AppleService object, which ensures + // that HaltOperation() can't execute while we're doing it. Because Java locks are re-entrant this + // locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent + // any other thread from stopping it until after the callback has completed and returned to us here. + + int result = BlockForData(); + synchronized (this) + { + if (fNativeContext == 0) break; // Some other thread stopped our DNSSD operation; time to terminate this thread + if (result == 0) continue; // If BlockForData() said there was no data, go back and block again + result = ProcessResults(); + if (fNativeContext == 0) break; // Event listener stopped its own DNSSD operation; terminate this thread + if (result != 0) { fListener.operationFailed(this, result); break; } // If error, notify listener + } + } + } + + protected BaseListener fListener; +} + + +class AppleBrowser extends AppleService +{ + public AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.CreateBrowser( flags, ifIndex, regType, domain)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateBrowser( int flags, int ifIndex, String regType, String domain); +} + +class AppleResolver extends AppleService +{ + public AppleResolver( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.CreateResolver( flags, ifIndex, serviceName, regType, domain)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateResolver( int flags, int ifIndex, String serviceName, String regType, + String domain); +} + +// An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord. +class AppleDNSRecord implements DNSRecord +{ + public AppleDNSRecord( AppleService owner) + { + fOwner = owner; + fRecord = 0; // record always starts out empty + } + + public void update( int flags, byte[] rData, int ttl) + throws DNSSDException + { + this.ThrowOnErr( this.Update( flags, rData, ttl)); + } + + public void remove() + throws DNSSDException + { + this.ThrowOnErr( this.Remove()); + } + + protected long fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ? + protected AppleService fOwner; + + protected void ThrowOnErr( int rc) throws DNSSDException + { + if ( rc != 0) + throw new AppleDNSSDException( rc); + } + + protected native int Update( int flags, byte[] rData, int ttl); + + protected native int Remove(); +} + +class AppleRegistration extends AppleService implements DNSSDRegistration +{ + public AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain, + String host, int port, byte[] txtRecord, RegisterListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.BeginRegister( ifIndex, flags, serviceName, regType, domain, host, port, txtRecord)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + public DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl) + throws DNSSDException + { + AppleDNSRecord newRecord = new AppleDNSRecord( this); + + this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord)); + return newRecord; + } + + public DNSRecord getTXTRecord() + throws DNSSDException + { + return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int BeginRegister( int ifIndex, int flags, String serviceName, String regType, + String domain, String host, int port, byte[] txtRecord); + + // Sets fNativeContext. Returns non-zero on error. + protected native int AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj); +} + +class AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar +{ + public AppleRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { + super(listener); + this.ThrowOnErr( this.CreateConnection()); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, + int rrclass, byte[] rdata, int ttl) + throws DNSSDException + { + AppleDNSRecord newRecord = new AppleDNSRecord( this); + + this.ThrowOnErr( this.RegisterRecord( flags, ifIndex, fullname, rrtype, rrclass, rdata, ttl, newRecord)); + return newRecord; + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateConnection(); + + // Sets fNativeContext. Returns non-zero on error. + protected native int RegisterRecord( int flags, int ifIndex, String fullname, int rrtype, + int rrclass, byte[] rdata, int ttl, AppleDNSRecord destObj); +} + +class AppleQuery extends AppleService +{ + public AppleQuery( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.CreateQuery( flags, ifIndex, serviceName, rrtype, rrclass)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass); +} + +class AppleDomainEnum extends AppleService +{ + public AppleDomainEnum( int flags, int ifIndex, DomainListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.BeginEnum( flags, ifIndex)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int BeginEnum( int flags, int ifIndex); +} + + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java new file mode 100644 index 0000000000..618128f31f --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java @@ -0,0 +1,76 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSSDException.java,v $ +Revision 1.4 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.3 2005/07/10 22:19:01 cheshire +Add missing error codes to list of public static final ints + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + +*/ + +package com.apple.dnssd; + + +/** + Used to report various DNS-SD-related error conditions. +*/ + +abstract public class DNSSDException extends Exception +{ + public static final int NO_ERROR = 0; + public static final int UNKNOWN = -65537; + public static final int NO_SUCH_NAME = -65538; + public static final int NO_MEMORY = -65539; + public static final int BAD_PARAM = -65540; + public static final int BAD_REFERENCE = -65541; + public static final int BAD_STATE = -65542; + public static final int BAD_FLAGS = -65543; + public static final int UNSUPPORTED = -65544; + public static final int NOT_INITIALIZED = -65545; + public static final int NO_CACHE = -65546; + public static final int ALREADY_REGISTERED = -65547; + public static final int NAME_CONFLICT = -65548; + public static final int INVALID = -65549; + public static final int FIREWALL = -65550; + public static final int INCOMPATIBLE = -65551; + public static final int BAD_INTERFACE_INDEX = -65552; + public static final int REFUSED = -65553; + public static final int NOSUCHRECORD = -65554; + public static final int NOAUTH = -65555; + public static final int NOSUCHKEY = -65556; + public static final int NATTRAVERSAL = -65557; + public static final int DOUBLENAT = -65558; + public static final int BADTIME = -65559; + public static final int BADSIG = -65560; + public static final int BADKEY = -65561; + public static final int TRANSIENT = -65562; + + /** Returns the sub-code that identifies the particular error. */ + abstract public int getErrorCode(); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java new file mode 100644 index 0000000000..366d83476b --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java @@ -0,0 +1,79 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + This file declares the public interface to DNSSDRecordRegistrar, a DNSSDService + subclass that allows efficient registration of multiple individual records. + + Change History (most recent first): + +$Log: DNSSDRecordRegistrar.java,v $ +Revision 1.2 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.1 2006/06/20 23:00:12 rpantos +<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** An object for registering records, created by {@link DNSSD#createRecordRegistrar}. */ + +public interface DNSSDRecordRegistrar extends DNSSDService +{ + /** Register an independent {@link DNSRecord}.<P> + @param flags + Possible values are SHARED or UNIQUE (see flag type definitions for details). + <P> + @param ifIndex + If non-zero, specifies the interface on which to register the record + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Passing 0 causes the record to be registered on all interfaces. + <P> + @param fullname + The full domain name of the resource record. + <P> + @param rrtype + The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc) + as defined in nameser.h. + <P> + @param rrclass + The class of the resource record, as defined in nameser.h + (usually 1 for the Internet class). + <P> + @param rData + The new rdata as it is to appear in the DNS record. + <P> + @param ttl + The time to live of the resource record, in seconds. Pass 0 to use a default value. + <P> + @param listener + This object will get called when the service is registered. + <P> + @return A {@link DNSSDService} that can be used to abort the record registration. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, + int rrclass, byte[] rdata, int ttl) + throws DNSSDException; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java new file mode 100644 index 0000000000..4ae38036ed --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java @@ -0,0 +1,77 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSSDRegistration.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/12/11 03:01:00 rpantos +<rdar://problem/3907498> Java DNSRecord API should be cleaned up + +Revision 1.1 2004/04/30 16:32:34 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + This file declares the public interface to DNSSDRegistration, a DNSSDService + subclass that allows a client to control a service registration. + */ + + +package com.apple.dnssd; + + +/** A tracking object for a registration created by {@link DNSSD#register}. */ + +public interface DNSSDRegistration extends DNSSDService +{ + /** Get a reference to the primary TXT record of a registered service.<P> + The record can be updated by sending it an update() message.<P> + + <P> + @return A {@link DNSRecord}. + If {@link DNSSDRegistration#stop} is called, the DNSRecord is also + invalidated and may not be used further. + */ + DNSRecord getTXTRecord() + throws DNSSDException; + + /** Add a record to a registered service.<P> + The name of the record will be the same as the registered service's name.<P> + The record can be updated or deregistered by sending it an update() or remove() message.<P> + + @param flags + Currently unused, reserved for future use. + <P> + @param rrType + The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h. + <P> + @param rData + The raw rdata to be contained in the added resource record. + <P> + @param ttl + The time to live of the resource record, in seconds. + <P> + @return A {@link DNSRecord} that may be passed to updateRecord() or removeRecord(). + If {@link DNSSDRegistration#stop} is called, the DNSRecord is also + invalidated and may not be used further. + */ + DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl) + throws DNSSDException; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java new file mode 100644 index 0000000000..93cc5fd2c9 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java @@ -0,0 +1,52 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSSDService.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:32:34 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + +/** A tracking object for a service created by {@link DNSSD}. */ + +public interface DNSSDService +{ + /** + Halt the active operation and free resources associated with the DNSSDService.<P> + + Any services or records registered with this DNSSDService will be deregistered. Any + Browse, Resolve, or Query operations associated with this reference will be terminated.<P> + + Note: if the service was initialized with DNSSD.register(), and an extra resource record was + added to the service via {@link DNSSDRegistration#addRecord}, the DNSRecord so created + is invalidated when this method is called - the DNSRecord may not be used afterward. + */ + void stop(); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java new file mode 100644 index 0000000000..276d5b539a --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java @@ -0,0 +1,75 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DomainListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** + A listener that receives results from {@link DNSSD#enumerateDomains}. +*/ + +public interface DomainListener extends BaseListener +{ + /** Called to report discovered domains.<P> + + @param domainEnum + The active domain enumerator. + @param flags + Possible values are: DNSSD.MORE_COMING, DNSSD.DEFAULT + <P> + @param ifIndex + Specifies the interface on which the domain exists. (The index for a given + interface is determined via the if_nametoindex() family of calls.) + <P> + @param domain + The name of the domain. + */ + void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain); + + /** Called to report that a domain has disappeared.<P> + + @param domainEnum + The active domain enumerator. + @param flags + Possible values are: DNSSD.MORE_COMING, DNSSD.DEFAULT + <P> + @param ifIndex + Specifies the interface on which the domain exists. (The index for a given + interface is determined via the if_nametoindex() family of calls.) + <P> + @param domain + The name of the domain. + */ + void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile b/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile new file mode 100644 index 0000000000..e25208d774 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile @@ -0,0 +1,150 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +DNSSD_PKG = com.apple.dnssd + +TOP = $(SRC)/lib/libdns_sd/java +JAVASRCDIR = $(TOP)/com/apple/dnssd +CLASSPATH = $(TOP):com/apple/dnssd + +JAVAFLAGS += -source 1.4 -target 1.4 +SOURCE:sh = ls *.java +CLASSES = $(SOURCE:java=class) +JNIH = DNSSD.java.h +JAR_FILE = dnssd.jar + +DOCDIR = $(JAVASRCDIR)/docs +DOCAPIDIR = $(JAVASRCDIR)/docs/api +DOCDESTDIR = $(ROOTDNSSDJAVAHOME)/javadoc/dnssd +DOCAPIDESTDIR = $(DOCDESTDIR)/api +DOCEXAMPLESDESTDIR = $(DOCDESTDIR)/examples + +EXAMPLESDIR = $(JAVASRCDIR)/docs/examples +EXAMPLESSRC = $(JAVASRCDIR)/docs/examples/src +SIMPLECHATOBJ = $(EXAMPLESDIR)/SwingBrowseListener.class \ + $(EXAMPLESDIR)/SwingQueryListener.class \ + $(EXAMPLESDIR)/SimpleChat.class +BROWSERAPPOBJ = $(EXAMPLESDIR)/SwingResolveListener.class \ + $(EXAMPLESDIR)/SwingDomainListener.class \ + $(EXAMPLESDIR)/BrowserApp.class +EXAMPLEOBJS = $(SIMPLECHATOBJ) $(BROWSERAPPOBJ) +EXAMPLEJARS = SimpleChat.jar BrowserApp.jar + +INSTALL_JAR = $(ROOTDNSSDJAVAHOME)/$(JAR_FILE) +INSTALL_EXAMPLEJARS = $(DOCEXAMPLESDESTDIR)/SimpleChat.jar \ + $(DOCEXAMPLESDESTDIR)/BrowserApp.jar + +CLEAN_FILES = *.class $(JNIH) *.jar $(EXAMPLESDIR)/*.class $(EXAMPLESDIR)/*.jar + +DEFINES= + +INCLUDES= -I${JAVA_HOME}/include \ + -I${JAVA_HOME}/include/solaris + +.KEEP_STATE: + +all: $(JNIH) $(CLASSES) $(EXAMPLEOBJS) doc + +install: $(CLASSES) $(ROOTDNSSDJAVAHOME) \ + $(DOCEXAMPLESDESTDIR) $(DOCEXAMPLESSRCDESTDIR) \ + $(JAR_FILE) $(INSTALL_JAR) $(JNIH) \ + $(EXAMPLEJARS) $(INSTALL_EXAMPLEJARS) \ + install_doc + +$(JNIH): $(CLASSES) + class="com.apple.dnssd.AppleDNSSD \ + com.apple.dnssd.AppleBrowser \ + com.apple.dnssd.AppleResolver \ + com.apple.dnssd.AppleRegistration \ + com.apple.dnssd.AppleQuery \ + com.apple.dnssd.AppleDomainEnum \ + com.apple.dnssd.AppleService"; \ + $(JAVAH) -classpath $(CLASSPATH) -jni -o $(JNIH) $$class + +clean clobber: + $(RM) $(CLEAN_FILES) + +$(JAR_FILE): $(CLASSES) + cd $(TOP); \ + $(JAR) -cvf $(TOP)/com/apple/dnssd/$(JAR_FILE) com/apple/dnssd/*.class + +$(EXAMPLESDIR)/%.class: $(EXAMPLESSRC)/%.java + $(JAVAC) $(JAVAFLAGS) $< -classpath $(CLASSPATH):$(EXAMPLESDIR) -d $(EXAMPLESDIR) + +SIMPLECHATMAN = $(EXAMPLESSRC)/SimpleChat.manifest + +SimpleChat.jar: $(SIMPLECHATOBJ) $(SIMPLECHATMAN) + cd $(EXAMPLESDIR); $(JAR) -cvfm $@ $(SIMPLECHATMAN) \ + SwingBrowseListener.class SwingQueryListener.class \ + SimpleChat.class SimpleChat\$$1.class \ + ListenerThread.class TargetListElem.class \ + TargetListModel.class src/SimpleChat.java \ + src/SimpleChat.manifest src/SwingBrowseListener.java \ + src/SwingQueryListener.java + +BROWSERAPPMAN = $(EXAMPLESSRC)/BrowserApp.manifest + +BrowserApp.jar: $(BROWSERAPPOBJ) $(BROWSERAPPMAN) + cd $(EXAMPLESDIR); $(JAR) -cvfm $@ $(BROWSERAPPMAN) \ + BrowserApp\$$1.class BrowserApp.class \ + BrowserListModel\$$BrowserListElem.class \ + BrowserListModel.class DomainListModel.class \ + ServicesBrowserListModel.class \ + SwingResolveListener.class SwingDomainListener.class \ + src/BrowserApp.java src/SwingResolveListener.java \ + src/SwingDomainListener.java src/BrowserApp.manifest + +$(ROOTDNSSDJAVAHOME): + $(INS.dir) + +$(ROOTDNSSDJAVAHOME)/%: % + $(INS.file) + +$(DOCDESTDIR): + $(INS.dir) + +$(DOCAPIDESTDIR): $(DOCDESTDIR) + $(INS.dir) + +$(DOCEXAMPLESDESTDIR): $(DOCDESTDIR) + $(INS.dir) + +$(DOCEXAMPLESDESTDIR)/%: % + $(RM) $@; $(INS) -s -m $(FILEMODE) -f $(@D) $(EXAMPLESDIR)/$< + +install_doc: $(CLASSES) $(DOCAPIDESTDIR) + -$(RM) -r $(DOCAPIDESTDIR)/* + cd $(TOP); umask 022; \ + $(JAVADOC) $(JAVASRCDIR)/*.java -classpath $(CLASSPATH) -d \ + $(DOCAPIDESTDIR) -public $(DNSSD_PKG) + +doc: + -@mkdir -p $(DOCAPIDIR) + cd $(TOP); umask 022; \ + $(JAVADOC) $(JAVASRCDIR)/*.java -classpath $(CLASSPATH) -d \ + $(DOCAPIDIR) -public $(DNSSD_PKG) diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java new file mode 100644 index 0000000000..7978dda42f --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java @@ -0,0 +1,71 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: QueryListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSD#queryRecord}. */ + +public interface QueryListener extends BaseListener +{ + /** Called when a record query has been completed.<P> + + @param query + The active query object. + <P> + @param flags + Possible values are DNSSD.MORE_COMING. + <P> + @param ifIndex + The interface on which the query was resolved. (The index for a given + interface is determined via the if_nametoindex() family of calls.) + <P> + @param fullName + The resource record's full domain name. + <P> + @param rrtype + The resource record's type (e.g. PTR, SRV, etc) as defined by RFC 1035 and its updates. + <P> + @param rrclass + The class of the resource record, as defined by RFC 1035 and its updates. + <P> + @param rdata + The raw rdata of the resource record. + <P> + @param ttl + The resource record's time to live, in seconds. + */ + void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java new file mode 100644 index 0000000000..d40b6f3e75 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java @@ -0,0 +1,64 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: RegisterListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSD#register}. */ + +public interface RegisterListener extends BaseListener +{ + /** Called when a registration has been completed.<P> + + @param registration + The active registration. + <P> + @param flags + Currently unused, reserved for future use. + <P> + @param serviceName + The service name registered (if the application did not specify a name in + DNSSD.register(), this indicates what name was automatically chosen). + <P> + @param regType + The type of service registered, as it was passed to DNSSD.register(). + <P> + @param domain + The domain on which the service was registered. If the application did not + specify a domain in DNSSD.register(), this is the default domain + on which the service was registered. + */ + void serviceRegistered( DNSSDRegistration registration, int flags, String serviceName, + String regType, String domain); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java new file mode 100644 index 0000000000..6b8c757ae3 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java @@ -0,0 +1,49 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: RegisterRecordListener.java,v $ +Revision 1.2 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.1 2006/06/20 23:00:12 rpantos +<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSDRecordRegistrar#registerRecord}. */ + +public interface RegisterRecordListener extends BaseListener +{ + /** Called when a record registration succeeds.<P> + + @param record + A {@link DNSRecord}. + <P> + @param flags + Currently ignored, reserved for future use. + <P> + */ + void recordRegistered( DNSRecord record, int flags); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java new file mode 100644 index 0000000000..4589f53915 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java @@ -0,0 +1,71 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: ResolveListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + +*/ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSD#resolve}. */ + +public interface ResolveListener extends BaseListener +{ + /** Called when a service has been resolved.<P> + + @param resolver + The active resolver object. + <P> + @param flags + Currently unused, reserved for future use. + <P> + @param fullName + The full service domain name, in the form <servicename>.<protocol>.<domain>. + (Any literal dots (".") are escaped with a backslash ("\."), and literal + backslashes are escaped with a second backslash ("\\"), e.g. a web server + named "Dr. Pepper" would have the fullname "Dr\.\032Pepper._http._tcp.local."). + This is the appropriate format to pass to standard system DNS APIs such as + res_query(), or to the special-purpose functions included in this API that + take fullname parameters. + <P> + @param hostName + The target hostname of the machine providing the service. This name can + be passed to functions like queryRecord() to look up the host's IP address. + <P> + @param port + The port number on which connections are accepted for this service. + <P> + @param txtRecord + The service's primary txt record. + */ + void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java new file mode 100644 index 0000000000..c1c5f0b3b7 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java @@ -0,0 +1,313 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: TXTRecord.java,v $ +Revision 1.6 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.5 2004/08/25 21:54:36 rpantos +<rdar://problem/3773973> Fix getValue() for values containing '='. + +Revision 1.4 2004/08/04 01:04:50 rpantos +<rdar://problems/3731579&3731582> Fix set(); add remove() & toString(). + +Revision 1.3 2004/07/13 21:24:25 rpantos +Fix for <rdar://problem/3701120>. + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + To do: + - implement remove() + - fix set() to replace existing values + */ + + +package com.apple.dnssd; + + +/** + Object used to construct and parse DNS-SD format TXT records. + For more info see <a href="http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt">DNS-Based Service Discovery</a>, section 6. +*/ + +public class TXTRecord +{ + /* + DNS-SD specifies that a TXT record corresponding to an SRV record consist of + a packed array of bytes, each preceded by a length byte. Each string + is an attribute-value pair. + + The TXTRecord object stores the entire TXT data as a single byte array, traversing it + as need be to implement its various methods. + */ + + static final protected byte kAttrSep = '='; + + protected byte[] fBytes; + + /** Constructs a new, empty TXT record. */ + public TXTRecord() + { fBytes = new byte[0]; } + + /** Constructs a new TXT record from a byte array in the standard format. */ + public TXTRecord( byte[] initBytes) + { fBytes = (byte[]) initBytes.clone(); } + + /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P> + @param key + The key name. Must be ASCII, with no '=' characters. + <P> + @param value + Value to be encoded into bytes using the default platform character set. + */ + public void set( String key, String value) + { + byte[] valBytes = (value != null) ? value.getBytes() : null; + this.set( key, valBytes); + } + + /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P> + @param key + The key name. Must be ASCII, with no '=' characters. + <P> + @param value + Binary representation of the value. + */ + public void set( String key, byte[] value) + { + byte[] keyBytes; + int valLen = (value != null) ? value.length : 0; + + try { + keyBytes = key.getBytes( "US-ASCII"); + } + catch ( java.io.UnsupportedEncodingException uee) { + throw new IllegalArgumentException(); + } + + for ( int i=0; i < keyBytes.length; i++) + if ( keyBytes[i] == '=') + throw new IllegalArgumentException(); + + if ( keyBytes.length + valLen >= 255) + throw new ArrayIndexOutOfBoundsException(); + + int prevLoc = this.remove( key); + if ( prevLoc == -1) + prevLoc = this.size(); + + this.insert( keyBytes, value, prevLoc); + } + + protected void insert( byte[] keyBytes, byte[] value, int index) + // Insert a key-value pair at index + { + byte[] oldBytes = fBytes; + int valLen = (value != null) ? value.length : 0; + int insertion = 0; + byte newLen, avLen; + + // locate the insertion point + for ( int i=0; i < index && insertion < fBytes.length; i++) + insertion += fBytes[ insertion] + 1; + + avLen = (byte) ( keyBytes.length + valLen + (value != null ? 1 : 0)); + newLen = (byte) ( avLen + oldBytes.length + 1); + + fBytes = new byte[ newLen]; + System.arraycopy( oldBytes, 0, fBytes, 0, insertion); + int secondHalfLen = oldBytes.length - insertion; + System.arraycopy( oldBytes, insertion, fBytes, newLen - secondHalfLen, secondHalfLen); + fBytes[ insertion] = avLen; + System.arraycopy( keyBytes, 0, fBytes, insertion + 1, keyBytes.length); + if ( value != null) + { + fBytes[ insertion + 1 + keyBytes.length] = kAttrSep; + System.arraycopy( value, 0, fBytes, insertion + keyBytes.length + 2, valLen); + } + } + + /** Remove a key/value pair from the TXT record. Returns index it was at, or -1 if not found. */ + public int remove( String key) + { + int avStart = 0; + + for ( int i=0; avStart < fBytes.length; i++) + { + int avLen = fBytes[ avStart]; + if ( key.length() <= avLen && + ( key.length() == avLen || fBytes[ avStart + key.length() + 1] == kAttrSep)) + { + String s = new String( fBytes, avStart + 1, key.length()); + if ( 0 == key.compareToIgnoreCase( s)) + { + byte[] oldBytes = fBytes; + fBytes = new byte[ oldBytes.length - avLen - 1]; + System.arraycopy( oldBytes, 0, fBytes, 0, avStart); + System.arraycopy( oldBytes, avStart + avLen + 1, fBytes, avStart, oldBytes.length - avStart - avLen - 1); + return i; + } + } + avStart += avLen + 1; + } + return -1; + } + + /** Return the number of keys in the TXT record. */ + public int size() + { + int i, avStart; + + for ( i=0, avStart=0; avStart < fBytes.length; i++) + avStart += fBytes[ avStart] + 1; + return i; + } + + /** Return true if key is present in the TXT record, false if not. */ + public boolean contains( String key) + { + String s = null; + + for ( int i=0; null != ( s = this.getKey( i)); i++) + if ( 0 == key.compareToIgnoreCase( s)) + return true; + return false; + } + + /** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */ + public String getKey( int index) + { + int avStart = 0; + + for ( int i=0; i < index && avStart < fBytes.length; i++) + avStart += fBytes[ avStart] + 1; + + if ( avStart < fBytes.length) + { + int avLen = fBytes[ avStart]; + int aLen = 0; + + for ( aLen=0; aLen < avLen; aLen++) + if ( fBytes[ avStart + aLen + 1] == kAttrSep) + break; + return new String( fBytes, avStart + 1, aLen); + } + return null; + } + + /** + Look up a key in the TXT record by zero-based index and return its value. <P> + Returns null if index exceeds the total number of keys. + Returns null if the key is present with no value. + */ + public byte[] getValue( int index) + { + int avStart = 0; + byte[] value = null; + + for ( int i=0; i < index && avStart < fBytes.length; i++) + avStart += fBytes[ avStart] + 1; + + if ( avStart < fBytes.length) + { + int avLen = fBytes[ avStart]; + int aLen = 0; + + for ( aLen=0; aLen < avLen; aLen++) + { + if ( fBytes[ avStart + aLen + 1] == kAttrSep) + { + value = new byte[ avLen - aLen - 1]; + System.arraycopy( fBytes, avStart + aLen + 2, value, 0, avLen - aLen - 1); + break; + } + } + } + return value; + } + + /** Converts the result of getValue() to a string in the platform default character set. */ + public String getValueAsString( int index) + { + byte[] value = this.getValue( index); + return value != null ? new String( value) : null; + } + + /** Get the value associated with a key. Will be null if the key is not defined. + Array will have length 0 if the key is defined with an = but no value.<P> + + @param forKey + The left-hand side of the key-value pair. + <P> + @return The binary representation of the value. + */ + public byte[] getValue( String forKey) + { + String s = null; + int i; + + for ( i=0; null != ( s = this.getKey( i)); i++) + if ( 0 == forKey.compareToIgnoreCase( s)) + return this.getValue( i); + return null; + } + + /** Converts the result of getValue() to a string in the platform default character set.<P> + + @param forKey + The left-hand side of the key-value pair. + <P> + @return The value represented in the default platform character set. + */ + public String getValueAsString( String forKey) + { + byte[] val = this.getValue( forKey); + return val != null ? new String( val) : null; + } + + /** Return the contents of the TXT record as raw bytes. */ + public byte[] getRawBytes() { return (byte[]) fBytes.clone(); } + + /** Return a string representation of the object. */ + public String toString() + { + String a, result = null; + + for ( int i=0; null != ( a = this.getKey( i)); i++) + { + String av = String.valueOf( i) + "={" + a; + String val = this.getValueAsString( i); + if ( val != null) + av += "=" + val + "}"; + else + av += "}"; + if ( result == null) + result = av; + else + result = result + ", " + av; + } + return result != null ? result : ""; + } +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java new file mode 100644 index 0000000000..381657151b --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java @@ -0,0 +1,400 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + BrowserApp demonstrates how to use DNS-SD to browse for and resolve services. + + To do: + - display resolved TXTRecord + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.text.*; +import javax.swing.*; +import javax.swing.event.*; + +import com.apple.dnssd.*; + + +class BrowserApp implements ListSelectionListener, ResolveListener +{ + static BrowserApp app; + JFrame frame; + DomainListModel domainList; + BrowserListModel servicesList, serviceList; + JList domainPane, servicesPane, servicePane; + DNSSDService servicesBrowser, serviceBrowser, domainBrowser; + JLabel hostLabel, portLabel; + + public BrowserApp() + { + frame = new JFrame("DNS-SD Service Browser"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + }); + + domainList = new DomainListModel(); + servicesList = new ServicesBrowserListModel(); + serviceList = new BrowserListModel(); + + try { + domainBrowser = DNSSD.enumerateDomains( DNSSD.BROWSE_DOMAINS, 0, domainList); + + servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList); + serviceBrowser = null; + } + catch ( Exception ex) { terminateWithException( ex); } + + this.setupSubPanes( frame.getContentPane()); + frame.pack(); + frame.setVisible(true); + } + + protected void setupSubPanes( Container parent) + { + parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS)); + + JPanel browserRow = new JPanel(); + browserRow.setLayout( new BoxLayout( browserRow, BoxLayout.X_AXIS)); + domainPane = new JList( domainList); + domainPane.addListSelectionListener( this); + JScrollPane domainScroller = new JScrollPane( domainPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( domainScroller); + servicesPane = new JList( servicesList); + servicesPane.addListSelectionListener( this); + JScrollPane servicesScroller = new JScrollPane( servicesPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( servicesScroller); + servicePane = new JList( serviceList); + servicePane.addListSelectionListener( this); + JScrollPane serviceScroller = new JScrollPane( servicePane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( serviceScroller); + +/* + JPanel buttonRow = new JPanel(); + buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS)); + buttonRow.add( Box.createHorizontalGlue()); + JButton connectButton = new JButton( "Don't Connect"); + buttonRow.add( connectButton); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); +*/ + + JPanel labelRow = new JPanel(); + labelRow.setLayout( new BoxLayout( labelRow, BoxLayout.X_AXIS)); + labelRow.add( new JLabel( " Host: ")); + hostLabel = new JLabel(); + labelRow.add( hostLabel); + labelRow.add( Box.createRigidArea( new Dimension( 32, 0))); + labelRow.add( new JLabel( "Port: ")); + portLabel = new JLabel(); + labelRow.add( portLabel); + labelRow.add( Box.createHorizontalGlue()); + + parent.add( browserRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( labelRow); +// parent.add( buttonRow); + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + } + + public void valueChanged( ListSelectionEvent e) + { + try { + if ( e.getSource() == domainPane && !e.getValueIsAdjusting()) + { + int newSel = domainPane.getSelectedIndex(); + if ( -1 != newSel) + { + if ( serviceBrowser != null) + serviceBrowser.stop(); + serviceList.removeAllElements(); + servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList); + } + } + else if ( e.getSource() == servicesPane && !e.getValueIsAdjusting()) + { + int newSel = servicesPane.getSelectedIndex(); + if ( serviceBrowser != null) + serviceBrowser.stop(); + serviceList.removeAllElements(); + if ( -1 != newSel) + serviceBrowser = DNSSD.browse( 0, 0, servicesList.getNthRegType( newSel), "", serviceList); + } + else if ( e.getSource() == servicePane && !e.getValueIsAdjusting()) + { + int newSel = servicePane.getSelectedIndex(); + + hostLabel.setText( ""); + portLabel.setText( ""); + + if ( -1 != newSel) + { + DNSSD.resolve( 0, serviceList.getNthInterface( newSel), + serviceList.getNthServiceName( newSel), + serviceList.getNthRegType( newSel), + serviceList.getNthDomain( newSel), + new SwingResolveListener( this)); + } + } + } + catch ( Exception ex) { terminateWithException( ex); } + } + + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + hostLabel.setText( hostName); + portLabel.setText( String.valueOf( port)); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } + + protected static void terminateWithException( Exception e) + { + e.printStackTrace(); + System.exit( -1); + } + + public static void main(String s[]) + { + app = new BrowserApp(); + } +} + + +class BrowserListModel extends DefaultListModel implements BrowseListener, Runnable +{ + public BrowserListModel() + { + addCache = new Vector(); + removeCache = new Vector(); + } + + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + addCache.add( new BrowserListElem( serviceName, domain, regType, ifIndex)); + if ( ( flags & DNSSD.MORE_COMING) == 0) + this.scheduleOnEventThread(); + } + + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + removeCache.add( serviceName); + if ( ( flags & DNSSD.MORE_COMING) == 0) + this.scheduleOnEventThread(); + } + + public void run() + { + while ( removeCache.size() > 0) + { + String serviceName = (String) removeCache.remove( removeCache.size() - 1); + int matchInd = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + if ( matchInd != -1) + this.removeElementAt( matchInd); + } + while ( addCache.size() > 0) + { + BrowserListElem elem = (BrowserListElem) addCache.remove( addCache.size() - 1); + if ( -1 == this.findMatching( elem.fServiceName)) // probably doesn't handle near-duplicates well. + this.addInSortOrder( elem); + } + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } + + /* The list contains BrowserListElem's */ + class BrowserListElem + { + public BrowserListElem( String serviceName, String domain, String type, int ifIndex) + { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; } + + public String toString() { return fServiceName; } + + public String fServiceName, fDomain, fType; + public int fInt; + } + + public String getNthServiceName( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fServiceName; + } + + public String getNthRegType( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fType; + } + + public String getNthDomain( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fDomain; + } + + public int getNthInterface( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fInt; + } + + protected void addInSortOrder( Object obj) + { + int i; + for ( i = 0; i < this.size(); i++) + if ( sCollator.compare( obj.toString(), this.getElementAt( i).toString()) < 0) + break; + this.add( i, obj); + } + + protected int findMatching( String match) + { + for ( int i = 0; i < this.size(); i++) + if ( match.equals( this.getElementAt( i).toString())) + return i; + return -1; + } + + protected void scheduleOnEventThread() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected Vector removeCache; // list of serviceNames to remove + protected Vector addCache; // list of BrowserListElem's to add + + protected static Collator sCollator; + + static // Initialize our static variables + { + sCollator = Collator.getInstance(); + sCollator.setStrength( Collator.PRIMARY); + } +} + + +class ServicesBrowserListModel extends BrowserListModel +{ + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + // Overridden to stuff serviceName into regType and make serviceName human-readable. + { + regType = serviceName + ( regType.startsWith( "_udp.") ? "._udp." : "._tcp."); + super.serviceFound( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain); + } + + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + // Overridden to make serviceName human-readable. + { + super.serviceLost( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain); + } + + protected String mapTypeToName( String type) + // Convert a registration type into a human-readable string. Returns original string on no-match. + { + final String[] namedServices = { + "_afpovertcp", "Apple File Sharing", + "_http", "World Wide Web servers", + "_daap", "Digital Audio Access", + "_apple-sasl", "Apple Password Servers", + "_distcc", "Distributed Compiler nodes", + "_finger", "Finger servers", + "_ichat", "iChat clients", + "_presence", "iChat AV clients", + "_ssh", "SSH servers", + "_telnet", "Telnet servers", + "_workstation", "Macintosh Manager clients", + "_bootps", "BootP servers", + "_xserveraid", "XServe RAID devices", + "_eppc", "Remote AppleEvents", + "_ftp", "FTP services", + "_tftp", "TFTP services" + }; + + for ( int i = 0; i < namedServices.length; i+=2) + if ( namedServices[i].equals( type)) + return namedServices[i + 1]; + return type; + } +} + + +class DomainListModel extends DefaultListModel implements DomainListener +{ + /* Called when a domain is discovered. */ + public void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + if ( !this.contains( domain)) + this.addElement( domain); + } + + public void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + if ( this.contains( domain)) + this.removeElement( domain); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest new file mode 100644 index 0000000000..472b17f638 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: BrowserApp +Class-Path: /usr/share/lib/java/dnssd.jar diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java new file mode 100644 index 0000000000..62dd11faa5 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java @@ -0,0 +1,336 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + SimpleChat is a simple peer-to-peer chat program that demonstrates + DNS-SD registration, browsing, resolving and record-querying. + + To do: + - implement better coloring algorithm + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import java.awt.*; +import java.awt.event.*; +import java.text.*; +import java.net.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +import com.apple.dnssd.*; + + +class SimpleChat implements ResolveListener, RegisterListener, QueryListener, + ActionListener, ItemListener, Runnable +{ + Document textDoc; // Holds all the chat text + JTextField inputField; // Holds a pending chat response + String ourName; // name used to identify this user in chat + DNSSDService browser; // object that actively browses for other chat clients + DNSSDService resolver; // object that resolves other chat clients + DNSSDRegistration registration; // object that maintains our connection advertisement + JComboBox targetPicker; // Indicates who we're talking to + TargetListModel targetList; // and its list model + JButton sendButton; // Will send text in inputField to target + InetAddress buddyAddr; // and address + int buddyPort; // and port + DatagramPacket dataPacket; // Inbound data packet + DatagramSocket outSocket; // Outbound data socket + SimpleAttributeSet textAttribs; + + static final String kChatExampleRegType = "_p2pchat._udp"; + static final String kWireCharSet = "ISO-8859-1"; + + public SimpleChat() throws Exception + { + JFrame frame = new JFrame("SimpleChat"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + }); + + ourName = System.getProperty( "user.name"); + targetList = new TargetListModel(); + textAttribs = new SimpleAttributeSet(); + DatagramSocket inSocket = new DatagramSocket(); + dataPacket = new DatagramPacket( new byte[ 4096], 4096); + outSocket = new DatagramSocket(); + + this.setupSubPanes( frame.getContentPane(), frame.getRootPane()); + frame.pack(); + frame.setVisible(true); + inputField.requestFocusInWindow(); + + browser = DNSSD.browse( 0, 0, kChatExampleRegType, "", new SwingBrowseListener( targetList)); + + registration = DNSSD.register( 0, 0, ourName, kChatExampleRegType, "", "", inSocket.getLocalPort(), null, this); + + new ListenerThread( this, inSocket, dataPacket).start(); + } + + protected void setupSubPanes( Container parent, JRootPane rootPane) + { + parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS)); + + JPanel textRow = new JPanel(); + textRow.setLayout( new BoxLayout( textRow, BoxLayout.X_AXIS)); + textRow.add( Box.createRigidArea( new Dimension( 16, 0))); + JEditorPane textPane = new JEditorPane( "text/html", "<BR>"); + textPane.setPreferredSize( new Dimension( 400, 300)); + textPane.setEditable( false); + JScrollPane textScroller = new JScrollPane( textPane); + textRow.add( textScroller); + textRow.add( Box.createRigidArea( new Dimension( 16, 0))); + textDoc = textPane.getDocument(); + + JPanel addressRow = new JPanel(); + addressRow.setLayout( new BoxLayout( addressRow, BoxLayout.X_AXIS)); + targetPicker = new JComboBox( targetList); + targetPicker.addItemListener( this); + addressRow.add( Box.createRigidArea( new Dimension( 16, 0))); + addressRow.add( new JLabel( "Talk to: ")); + addressRow.add( targetPicker); + addressRow.add( Box.createHorizontalGlue()); + + JPanel buttonRow = new JPanel(); + buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS)); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); + inputField = new JTextField(); + // prevent inputField from hijacking <Enter> key + inputField.getKeymap().removeKeyStrokeBinding( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0)); + buttonRow.add( inputField); + sendButton = new JButton( "Send"); + buttonRow.add( Box.createRigidArea( new Dimension( 8, 0))); + buttonRow.add( sendButton); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); + rootPane.setDefaultButton( sendButton); + sendButton.addActionListener( this); + sendButton.setEnabled( false); + + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + parent.add( textRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( addressRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( buttonRow); + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + } + + public void serviceRegistered( DNSSDRegistration registration, int flags, + String serviceName, String regType, String domain) + { + ourName = serviceName; // might have been renamed on collision + } + + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( "Service reported error " + String.valueOf( errorCode)); + } + + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + buddyPort = port; + try { + // Start a record query to obtain IP address from hostname + DNSSD.queryRecord( 0, ifIndex, hostName, 1 /* ns_t_a */, 1 /* ns_c_in */, + new SwingQueryListener( this)); + } + catch ( Exception e) { terminateWithException( e); } + resolver.stop(); + } + + public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl) + { + try { + buddyAddr = InetAddress.getByAddress( rdata); + } + catch ( Exception e) { terminateWithException( e); } + sendButton.setEnabled( true); + } + + public void actionPerformed( ActionEvent e) // invoked when Send button is hit + { + try + { + String sendString = ourName + ": " + inputField.getText(); + byte[] sendData = sendString.getBytes( kWireCharSet); + outSocket.send( new DatagramPacket( sendData, sendData.length, buddyAddr, buddyPort)); + StyleConstants.setForeground( textAttribs, Color.black); + textDoc.insertString( textDoc.getLength(), inputField.getText() + "\n", textAttribs); + inputField.setText( ""); + } + catch ( Exception exception) { terminateWithException( exception); } + } + + public void itemStateChanged( ItemEvent e) // invoked when Target selection changes + { + sendButton.setEnabled( false); + if ( e.getStateChange() == ItemEvent.SELECTED) + { + try { + TargetListElem sel = (TargetListElem) targetList.getSelectedItem(); + resolver = DNSSD.resolve( 0, sel.fInt, sel.fServiceName, sel.fType, sel.fDomain, this); + } + catch ( Exception exception) { terminateWithException( exception); } + } + } + + public void run() // invoked on event thread when inbound packet arrives + { + try + { + String inMessage = new String( dataPacket.getData(), 0, dataPacket.getLength(), kWireCharSet); + StyleConstants.setForeground( textAttribs, this.getColorFor( dataPacket.getData(), dataPacket.getLength())); + textDoc.insertString( textDoc.getLength(), inMessage + "\n", textAttribs); + } + catch ( Exception e) { terminateWithException( e); } + } + + protected Color getColorFor( byte[] chars, int length) + // Produce a mapping from a string to a color, suitable for text display + { + int rgb = 0; + for ( int i=0; i < length && chars[i] != ':'; i++) + rgb = rgb ^ ( (int) chars[i] << (i%3+2) * 8); + return new Color( rgb & 0x007F7FFF); // mask off high bits so it is a dark color + +// for ( int i=0; i < length && chars[i] != ':'; i++) + + } + + protected static void terminateWithException( Exception e) + { + e.printStackTrace(); + System.exit( -1); + } + + public static void main(String s[]) + { + try { + new SimpleChat(); + } + catch ( Exception e) { terminateWithException( e); } + } +} + + + +class TargetListElem +{ + public TargetListElem( String serviceName, String domain, String type, int ifIndex) + { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; } + + public String toString() { return fServiceName; } + + public String fServiceName, fDomain, fType; + public int fInt; +} + +class TargetListModel extends DefaultComboBoxModel implements BrowseListener +{ + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + + if ( match == null) + this.addElement( new TargetListElem( serviceName, domain, regType, ifIndex)); + } + + /* The Browser invokes this callback when a service disappears. */ + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + + if ( match != null) + this.removeElement( match); + } + + /* The Browser invokes this callback when a service disappears. */ + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( "Service reported error " + String.valueOf( errorCode)); + } + + protected TargetListElem findMatching( String match) + { + for ( int i = 0; i < this.getSize(); i++) + if ( match.equals( this.getElementAt( i).toString())) + return (TargetListElem) this.getElementAt( i); + return null; + } + +} + + +// A ListenerThread runs its owner when datagram packet p appears on socket s. +class ListenerThread extends Thread +{ + public ListenerThread( Runnable owner, DatagramSocket s, DatagramPacket p) + { fOwner = owner; fSocket = s; fPacket = p; } + + public void run() + { + while ( true ) + { + try + { + fSocket.receive( fPacket); + SwingUtilities.invokeAndWait( fOwner); // process data on main thread + } + catch( Exception e) + { + break; // terminate thread + } + } + } + + protected Runnable fOwner; + protected DatagramSocket fSocket; + protected DatagramPacket fPacket; +} + + + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest new file mode 100644 index 0000000000..71ca6a754e --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: SimpleChat +Class-Path: /usr/share/lib/java/dnssd.jar diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java new file mode 100644 index 0000000000..c8ff1e6ca1 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java @@ -0,0 +1,126 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ident "%Z%%M% %I% %E% SMI" + + */ + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule BrowseListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingBrowseListener implements Runnable, BrowseListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingBrowseListener( BrowseListener listener) + { fListener = listener; fErrorCode = 0; } + + /** (Clients should not call this method directly.) */ + public void operationFailed( DNSSDService service, int errorCode) + { + fBrowser = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + + { + fBrowser = browser; + fIsAdd = true; + fFlags = flags; + fIndex = ifIndex; + fService = serviceName; + fRegType = regType; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + fBrowser = browser; + fIsAdd = false; + fFlags = flags; + fIndex = ifIndex; + fService = serviceName; + fRegType = regType; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fBrowser, fErrorCode); + else if ( fIsAdd) + fListener.serviceFound( fBrowser, fFlags, fIndex, fService, fRegType, fDomain); + else + fListener.serviceLost( fBrowser, fFlags, fIndex, fService, fRegType, fDomain); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected BrowseListener fListener; + + protected boolean fIsAdd; + protected DNSSDService fBrowser; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fService; + protected String fRegType; + protected String fDomain; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java new file mode 100644 index 0000000000..988591ccfc --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java @@ -0,0 +1,119 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule DomainListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingDomainListener implements Runnable, DomainListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingDomainListener( DomainListener listener) + { fListener = listener; fErrorCode = 0; } + + /** (Clients should not call this method directly.) */ + public void operationFailed( DNSSDService service, int errorCode) + { + fEnumerator = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain) + + { + fEnumerator = domainEnum; + fIsAdd = true; + fFlags = flags; + fIndex = ifIndex; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + fEnumerator = domainEnum; + fIsAdd = false; + fFlags = flags; + fIndex = ifIndex; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fEnumerator, fErrorCode); + else if ( fIsAdd) + fListener.domainFound( fEnumerator, fFlags, fIndex, fDomain); + else + fListener.domainLost( fEnumerator, fFlags, fIndex, fDomain); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected DomainListener fListener; + + protected boolean fIsAdd; + protected DNSSDService fEnumerator; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fDomain; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java new file mode 100644 index 0000000000..1b301ce8e9 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java @@ -0,0 +1,111 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule QueryListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingQueryListener implements Runnable, QueryListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingQueryListener( QueryListener listener) + { fListener = listener; } + + public void operationFailed( DNSSDService service, int errorCode) + { + fQuery = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl) + { + fQuery = query; + fFlags = flags; + fIndex = ifIndex; + fFullName = fullName; + fType = rrtype; + fClass = rrclass; + fData = rdata; + fTTL = ttl; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fQuery, fErrorCode); + else + fListener.queryAnswered( fQuery, fFlags, fIndex, fFullName, fType, fClass, fData, fTTL); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected QueryListener fListener; + + protected DNSSDService fQuery; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fFullName; + protected int fType; + protected int fClass; + protected byte[] fData; + protected int fTTL; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java new file mode 100644 index 0000000000..499da0707d --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java @@ -0,0 +1,109 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule ResolveListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingResolveListener implements Runnable, ResolveListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingResolveListener( ResolveListener listener) + { fListener = listener; } + + public void operationFailed( DNSSDService service, int errorCode) + { + fResolver = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + fResolver = resolver; + fFlags = flags; + fIndex = ifIndex; + fFullName = fullName; + fHostName = hostName; + fPort = port; + fTXTRecord = txtRecord; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fResolver, fErrorCode); + else + fListener.serviceResolved( fResolver, fFlags, fIndex, fFullName, fHostName, fPort, fTXTRecord); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected ResolveListener fListener; + + protected DNSSDService fResolver; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fFullName; + protected String fHostName; + protected int fPort; + protected TXTRecord fTXTRecord; +} + diff --git a/usr/src/lib/libdns_sd/java/common/JNISupport.c b/usr/src/lib/libdns_sd/java/common/JNISupport.c new file mode 100644 index 0000000000..bdf453475d --- /dev/null +++ b/usr/src/lib/libdns_sd/java/common/JNISupport.c @@ -0,0 +1,1107 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: JNISupport.c,v $ +Revision 1.17 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.16 2006/07/14 02:35:47 cheshire +Added (commented out) syslog debugging messages + +Revision 1.15 2006/06/27 19:34:43 cheshire +<rdar://problem/4430023> txtRecord parameter of DNSServiceResolveReply() should be unsigned char * + +Revision 1.14 2006/06/20 23:03:35 rpantos +<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent + +Revision 1.13 2005/10/26 01:52:24 cheshire +<rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux) + +Revision 1.12 2005/07/13 19:20:32 cheshire +<rdar://problem/4175511> Race condition in Java API +Additional cleanup suggested by Roger -- NewContext() doesn't need ownerClass parameter any more + +Revision 1.11 2005/07/11 01:55:21 cheshire +<rdar://problem/4175511> Race condition in Java API + +Revision 1.10 2005/07/05 13:01:52 cheshire +<rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time + +Revision 1.9 2004/12/11 03:01:00 rpantos +<rdar://problem/3907498> Java DNSRecord API should be cleaned up + +Revision 1.8 2004/11/30 23:51:05 cheshire +Remove double semicolons + +Revision 1.7 2004/11/23 08:12:04 shersche +Implement if_nametoindex and if_indextoname for Win32 platforms + +Revision 1.6 2004/11/23 03:41:14 cheshire +Change JNISupport.c to call if_indextoname & if_nametoindex directly. +(May require some additional glue code to work on Windows.) + +Revision 1.5 2004/11/17 17:07:44 cheshire +Updated comment about AUTO_CALLBACKS + +Revision 1.4 2004/11/12 03:23:09 rpantos +rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex. + +Revision 1.3 2004/06/18 04:44:17 rpantos +Adapt to API unification on Windows + +Revision 1.2 2004/05/28 23:34:42 ksekar +<rdar://problem/3672903>: Java project build errors + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + + + This file contains the platform support for DNSSD and related Java classes. + It is used to shim through to the underlying <dns_sd.h> API. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +// AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response +// callbacks automatically (as in the early Windows prototypes). +// AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to +// invoke response callbacks (as is true on Mac OS X, Posix, Windows, etc.). +// (Invoking callbacks automatically on a different thread sounds attractive, but while +// the client gains by not needing to add an event source to its main event loop, it loses +// by being forced to deal with concurrency and locking, which can be a bigger burden.) +#ifndef AUTO_CALLBACKS +#define AUTO_CALLBACKS 0 +#endif + +#if !AUTO_CALLBACKS +#ifdef _WIN32 +#include <winsock2.h> +#else //_WIN32 +#include <sys/types.h> +#include <sys/select.h> +#endif // _WIN32 +#endif // AUTO_CALLBACKS + +#include <dns_sd.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _WIN32 +#include <winsock2.h> +#include <iphlpapi.h> +static char * if_indextoname( DWORD ifIndex, char * nameBuff); +static DWORD if_nametoindex( const char * nameStr ); +#define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH +#else // _WIN32 +#include <sys/socket.h> +#include <net/if.h> +#endif // _WIN32 +#include <jni.h> + +#include "DNSSD.java.h" + +//#include <syslog.h> + +// convenience definition +#ifdef __GNUC__ +#define _UNUSED __attribute__ ((unused)) +#else +#define _UNUSED +#endif + +enum { + kInterfaceVersion = 1 // Must match version in .jar file +}; + +typedef struct OpContext OpContext; + +struct OpContext +{ + DNSServiceRef ServiceRef; + JNIEnv *Env; + jobject JavaObj; + jobject ClientObj; + jmethodID Callback; + jmethodID Callback2; +}; + +// For AUTO_CALLBACKS, we must attach the callback thread to the Java VM prior to upcall. +#if AUTO_CALLBACKS +JavaVM *gJavaVM = NULL; +#endif + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv, jclass cls, + jint callerVersion) +{ + /* Ensure that caller & interface versions match. */ + if ( callerVersion != kInterfaceVersion) + return kDNSServiceErr_Incompatible; + +#if AUTO_CALLBACKS + { + jsize numVMs; + + if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs)) + return kDNSServiceErr_BadState; + } +#endif + + // Set AppleDNSSD.hasAutoCallbacks + { +#if AUTO_CALLBACKS + jboolean hasAutoC = JNI_TRUE; +#else + jboolean hasAutoC = JNI_FALSE; +#endif + jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z"); + (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC); + } + + return kDNSServiceErr_NoError; +} + + +static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str) +// Wrapper for JNI GetStringUTFChars() that returns NULL for null str. +{ + return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL; +} + +static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff) +// Wrapper for JNI GetStringUTFChars() that handles null str. +{ + if ( str != NULL) + (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff); +} + + +#if AUTO_CALLBACKS +static void SetupCallbackState( JNIEnv **ppEnv) +{ + (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL); +} + +static void TeardownCallbackState( void ) +{ + (*gJavaVM)->DetachCurrentThread( gJavaVM); +} + +#else // AUTO_CALLBACKS + +static void SetupCallbackState( JNIEnv **ppEnv _UNUSED) +{ + // No setup necessary if ProcessResults() has been called +} + +static void TeardownCallbackState( void ) +{ + // No teardown necessary if ProcessResults() has been called +} +#endif // AUTO_CALLBACKS + + +static OpContext *NewContext( JNIEnv *pEnv, jobject owner, + const char *callbackName, const char *callbackSig) +// Create and initialize a new OpContext. +{ + OpContext *pContext = (OpContext*) malloc( sizeof *pContext); + + if ( pContext != NULL) + { + jfieldID clientField = (*pEnv)->GetFieldID( pEnv, (*pEnv)->GetObjectClass( pEnv, owner), + "fListener", "Lcom/apple/dnssd/BaseListener;"); + + pContext->JavaObj = (*pEnv)->NewWeakGlobalRef( pEnv, owner); // must convert local ref to global to cache; + pContext->ClientObj = (*pEnv)->GetObjectField( pEnv, owner, clientField); + pContext->ClientObj = (*pEnv)->NewWeakGlobalRef( pEnv, pContext->ClientObj); // must convert local ref to global to cache + pContext->Callback = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + callbackName, callbackSig); + pContext->Callback2 = NULL; // not always used + } + + return pContext; +} + + +static void ReportError( JNIEnv *pEnv, jobject target, jobject service, DNSServiceErrorType err) +// Invoke operationFailed() method on target with err. +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, target); + jmethodID opFailed = (*pEnv)->GetMethodID( pEnv, cls, "operationFailed", + "(Lcom/apple/dnssd/DNSSDService;I)V"); + + (*pEnv)->CallVoidMethod( pEnv, target, opFailed, service, err); +} + +JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv *pEnv, jobject pThis) +/* Deallocate the dns_sd service browser and set the Java object's fNativeContext field to 0. */ +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + + if ( contextField != 0) + { + OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + if ( pContext != NULL) + { + // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate() + (*pEnv)->SetLongField( pEnv, pThis, contextField, 0); + if ( pContext->ServiceRef != NULL) + DNSServiceRefDeallocate( pContext->ServiceRef); + + (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->JavaObj); + (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->ClientObj); + free( pContext); + } + } +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *pEnv, jobject pThis) +/* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */ +{ +// BlockForData() not supported with AUTO_CALLBACKS +#if !AUTO_CALLBACKS + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + + if ( contextField != 0) + { + OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + if ( pContext != NULL) + { + fd_set readFDs; + int sd = DNSServiceRefSockFD( pContext->ServiceRef); + struct timeval timeout = { 1, 0 }; + FD_ZERO( &readFDs); + FD_SET( sd, &readFDs); + + // Q: Why do we poll here? + // A: Because there's no other thread-safe way to do it. + // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not, + // and arguably Linux is correct (See <http://www.ussg.iu.edu/hypermail/linux/kernel/0405.1/0418.html>) + // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while + // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way + // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously. + // If we try to do this without holding any lock, then right as we jump to the select() routine, + // some other thread could stop our operation (thereby closing the socket), + // and then that thread (or even some third, unrelated thread) + // could do some other DNS-SD operation (or some other operation that opens a new file descriptor) + // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor + // that may coincidentally have the same numerical value, but is semantically unrelated + // to the true file descriptor we thought we were blocking on. + // We can't stop this race condition from happening, but at least if we wake up once a second we can detect + // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd. + + if (select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout) == 1) return(1); + } + } +#endif // !AUTO_CALLBACKS + return(0); +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv *pEnv, jobject pThis) +/* Call through to DNSServiceProcessResult() while data remains on socket. */ +{ +#if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS + + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + DNSServiceErrorType err = kDNSServiceErr_BadState; + + if ( pContext != NULL) + { + int sd = DNSServiceRefSockFD( pContext->ServiceRef); + fd_set readFDs; + struct timeval zeroTimeout = { 0, 0 }; + + pContext->Env = pEnv; + + FD_ZERO( &readFDs); + FD_SET( sd, &readFDs); + + err = kDNSServiceErr_NoError; + if (0 < select(sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)) + { + err = DNSServiceProcessResult(pContext->ServiceRef); + // Use caution here! + // We cannot touch any data structures associated with this operation! + // The DNSServiceProcessResult() routine should have invoked our callback, + // and our callback could have terminated the operation with op.stop(); + // and that means HaltOperation() will have been called, which frees pContext. + // Basically, from here we just have to get out without touching any stale + // data structures that could blow up on us! Particularly, any attempt + // to loop here reading more results from the file descriptor is unsafe. + } + } + return err; +#endif // AUTO_CALLBACKS +} + + +static void DNSSD_API ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, + const char *replyDomain, void *context) +{ + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, + ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + (*pContext->Env)->NewStringUTF( pContext->Env, regtype), + (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring regType, jstring domain) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceFound", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + + pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + + err = DNSServiceBrowse( &pContext->ServiceRef, flags, ifIndex, regStr, domainStr, ServiceBrowseReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + + +static void DNSSD_API ServiceResolveReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, + uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) +{ + OpContext *pContext = (OpContext*) context; + jclass txtCls; + jmethodID txtCtor; + jbyteArray txtBytes; + jobject txtObj; + jbyte *pBytes; + + SetupCallbackState( &pContext->Env); + + txtCls = (*pContext->Env)->FindClass( pContext->Env, "com/apple/dnssd/TXTRecord"); + txtCtor = (*pContext->Env)->GetMethodID( pContext->Env, txtCls, "<init>", "([B)V"); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL && txtCtor != NULL && + NULL != ( txtBytes = (*pContext->Env)->NewByteArray( pContext->Env, txtLen))) + { + if ( errorCode == kDNSServiceErr_NoError) + { + // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit + // pattern into a number here. + port = ( ((unsigned char*) &port)[0] << 8) | ((unsigned char*) &port)[1]; + + // Initialize txtBytes with contents of txtRecord + pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, txtBytes, NULL); + memcpy( pBytes, txtRecord, txtLen); + (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, txtBytes, pBytes, JNI_COMMIT); + + // Construct txtObj with txtBytes + txtObj = (*pContext->Env)->NewObject( pContext->Env, txtCls, txtCtor, txtBytes); + (*pContext->Env)->DeleteLocalRef( pContext->Env, txtBytes); + + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, fullname), + (*pContext->Env)->NewStringUTF( pContext->Env, hosttarget), + port, txtObj); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring serviceName, jstring regType, jstring domain) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceResolved", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + + err = DNSServiceResolve( &pContext->ServiceRef, flags, ifIndex, + servStr, regStr, domainStr, ServiceResolveReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + + +static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, + DNSServiceErrorType errorCode, const char *serviceName, + const char *regType, const char *domain, void *context) +{ + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + (*pContext->Env)->NewStringUTF( pContext->Env, regType), + (*pContext->Env)->NewStringUTF( pContext->Env, domain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_BeginRegister( JNIEnv *pEnv, jobject pThis, + jint ifIndex, jint flags, jstring serviceName, jstring regType, + jstring domain, jstring host, jint port, jbyteArray txtRecord) +{ + //syslog(LOG_ERR, "BR"); + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + + //syslog(LOG_ERR, "BR: contextField %d", contextField); + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceRegistered", + "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + const char *hostStr = SafeGetUTFChars( pEnv, host); + + //syslog(LOG_ERR, "BR: regStr %s", regStr); + + // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a + // big-endian number into a 16-bit pattern here. + uint16_t portBits = port; + portBits = ( ((unsigned char*) &portBits)[0] << 8) | ((unsigned char*) &portBits)[1]; + + pBytes = txtRecord ? (*pEnv)->GetByteArrayElements( pEnv, txtRecord, NULL) : NULL; + numBytes = txtRecord ? (*pEnv)->GetArrayLength( pEnv, txtRecord) : 0; + + err = DNSServiceRegister( &pContext->ServiceRef, flags, ifIndex, servStr, regStr, + domainStr, hostStr, portBits, + numBytes, pBytes, ServiceRegisterReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, txtRecord, pBytes, 0); + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + SafeReleaseUTFChars( pEnv, host, hostStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv *pEnv, jobject pThis, + jint flags, jint rrType, jbyteArray rData, jint ttl, jobject destObj) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef; + + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceAddRecord( pContext->ServiceRef, &recRef, flags, rrType, numBytes, pBytes, ttl); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, destObj, recField, (jlong) recRef); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + return err; +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis, + jint flags, jbyteArray rData, jint ttl) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef = NULL; + + if ( ownerField != 0) + { + jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); + jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, ownerObj, contextField); + } + if ( recField != 0) + recRef = (DNSRecordRef) (*pEnv)->GetLongField( pEnv, pThis, recField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceUpdateRecord( pContext->ServiceRef, recRef, flags, numBytes, pBytes, ttl); + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + return err; +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + DNSRecordRef recRef = NULL; + + if ( ownerField != 0) + { + jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); + jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, ownerObj, contextField); + } + if ( recField != 0) + recRef = (DNSRecordRef) (*pEnv)->GetLongField( pEnv, pThis, recField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0); + + return err; +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection( JNIEnv *pEnv, jobject pThis) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + err = DNSServiceCreateConnection( &pContext->ServiceRef); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + +struct RecordRegistrationRef +{ + OpContext *Context; + jobject RecordObj; +}; +typedef struct RecordRegistrationRef RecordRegistrationRef; + +static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED, + DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags, + DNSServiceErrorType errorCode, void *context) +{ + RecordRegistrationRef *regEnvelope = (RecordRegistrationRef*) context; + OpContext *pContext = regEnvelope->Context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + regEnvelope->RecordObj, flags); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + (*pContext->Env)->DeleteWeakGlobalRef( pContext->Env, regEnvelope->RecordObj); + free( regEnvelope); + + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring fullname, jint rrType, jint rrClass, + jbyteArray rData, jint ttl, jobject destObj) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); + const char *nameStr = SafeGetUTFChars( pEnv, fullname); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef; + RecordRegistrationRef *regEnvelope; + + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + if ( pContext == NULL || pContext->ServiceRef == NULL || nameStr == NULL) + return kDNSServiceErr_BadParam; + + regEnvelope = calloc( 1, sizeof *regEnvelope); + if ( regEnvelope == NULL) + return kDNSServiceErr_NoMemory; + regEnvelope->Context = pContext; + regEnvelope->RecordObj = (*pEnv)->NewWeakGlobalRef( pEnv, destObj); // must convert local ref to global to cache + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceRegisterRecord( pContext->ServiceRef, &recRef, flags, ifIndex, + nameStr, rrType, rrClass, numBytes, pBytes, ttl, + RegisterRecordReply, regEnvelope); + + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, destObj, recField, (jlong) recRef); + } + else + { + if ( regEnvelope->RecordObj != NULL) + (*pEnv)->DeleteWeakGlobalRef( pEnv, regEnvelope->RecordObj); + free( regEnvelope); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + SafeReleaseUTFChars( pEnv, fullname, nameStr); + + return err; +} + + +static void DNSSD_API ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, + uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, + const void *rdata, uint32_t ttl, void *context) +{ + OpContext *pContext = (OpContext*) context; + jbyteArray rDataObj; + jbyte *pBytes; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL && + NULL != ( rDataObj = (*pContext->Env)->NewByteArray( pContext->Env, rdlen))) + { + if ( errorCode == kDNSServiceErr_NoError) + { + // Initialize rDataObj with contents of rdata + pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, rDataObj, NULL); + memcpy( pBytes, rdata, rdlen); + (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, rDataObj, pBytes, JNI_COMMIT); + + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + rrtype, rrclass, rDataObj, ttl); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring serviceName, jint rrtype, jint rrclass) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "queryAnswered", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + + err = DNSServiceQueryRecord( &pContext->ServiceRef, flags, ifIndex, servStr, + rrtype, rrclass, ServiceQueryReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + + +static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) +{ + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, + ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDomainEnum_BeginEnum( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "domainFound", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); + + err = DNSServiceEnumerateDomains( &pContext->ServiceRef, flags, ifIndex, + DomainEnumReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_ConstructName( JNIEnv *pEnv, jobject pThis _UNUSED, + jstring serviceName, jstring regtype, jstring domain, jobjectArray pOut) +{ + DNSServiceErrorType err = kDNSServiceErr_NoError; + const char *nameStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regtype); + const char *domStr = SafeGetUTFChars( pEnv, domain); + char buff[ kDNSServiceMaxDomainName + 1]; + + err = DNSServiceConstructFullName( buff, nameStr, regStr, domStr); + + if ( err == kDNSServiceErr_NoError) + { + // pOut is expected to be a String[1] array. + (*pEnv)->SetObjectArrayElement( pEnv, pOut, 0, (*pEnv)->NewStringUTF( pEnv, buff)); + } + + SafeReleaseUTFChars( pEnv, serviceName, nameStr); + SafeReleaseUTFChars( pEnv, regtype, regStr); + SafeReleaseUTFChars( pEnv, domain, domStr); + + return err; +} + +JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv *pEnv, jobject pThis _UNUSED, + jint flags, jint ifIndex, jstring fullName, + jint rrtype, jint rrclass, jbyteArray rdata) +{ + jbyte *pBytes; + jsize numBytes; + const char *nameStr = SafeGetUTFChars( pEnv, fullName); + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rdata); + + DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes); + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0); + + SafeReleaseUTFChars( pEnv, fullName, nameStr); +} + +#define LOCAL_ONLY_NAME "loo" + +JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED, + jint ifIndex) +{ + char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE]; + + if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly) + p = if_indextoname( ifIndex, nameBuff ); + + return (*pEnv)->NewStringUTF( pEnv, p); +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED, + jstring ifName) +{ + uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly; + const char *nameStr = SafeGetUTFChars( pEnv, ifName); + + if (strcmp(nameStr, LOCAL_ONLY_NAME)) + ifIndex = if_nametoindex( nameStr); + + SafeReleaseUTFChars( pEnv, ifName, nameStr); + + return ifIndex; +} + + +#if defined(_WIN32) +static char* +if_indextoname( DWORD ifIndex, char * nameBuff) +{ + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + DWORD dwRetVal = 0; + char * ifName = NULL; + ULONG ulOutBufLen = 0; + + if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) + { + goto exit; + } + + pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); + + if (pAdapterInfo == NULL) + { + goto exit; + } + + dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen ); + + if (dwRetVal != NO_ERROR) + { + goto exit; + } + + pAdapter = pAdapterInfo; + while (pAdapter) + { + if (pAdapter->Index == ifIndex) + { + // It would be better if we passed in the length of nameBuff to this + // function, so we would have absolute certainty that no buffer + // overflows would occur. Buffer overflows *shouldn't* occur because + // nameBuff is of size MAX_ADAPTER_NAME_LENGTH. + strcpy( nameBuff, pAdapter->AdapterName ); + ifName = nameBuff; + break; + } + + pAdapter = pAdapter->Next; + } + +exit: + + if (pAdapterInfo != NULL) + { + free( pAdapterInfo ); + pAdapterInfo = NULL; + } + + return ifName; +} + + +static DWORD +if_nametoindex( const char * nameStr ) +{ + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + DWORD dwRetVal = 0; + DWORD ifIndex = 0; + ULONG ulOutBufLen = 0; + + if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) + { + goto exit; + } + + pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); + + if (pAdapterInfo == NULL) + { + goto exit; + } + + dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen ); + + if (dwRetVal != NO_ERROR) + { + goto exit; + } + + pAdapter = pAdapterInfo; + while (pAdapter) + { + if (strcmp(pAdapter->AdapterName, nameStr) == 0) + { + ifIndex = pAdapter->Index; + break; + } + + pAdapter = pAdapter->Next; + } + +exit: + + if (pAdapterInfo != NULL) + { + free( pAdapterInfo ); + pAdapterInfo = NULL; + } + + return ifIndex; +} +#endif diff --git a/usr/src/lib/libdns_sd/java/common/mapfile-vers b/usr/src/lib/libdns_sd/java/common/mapfile-vers new file mode 100644 index 0000000000..0ae50ad6ff --- /dev/null +++ b/usr/src/lib/libdns_sd/java/common/mapfile-vers @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +SUNW_1.1 { + global: + Java_com_apple_dnssd_AppleBrowser_CreateBrowser; + Java_com_apple_dnssd_AppleDNSRecord_Remove; + Java_com_apple_dnssd_AppleDNSRecord_Update; + Java_com_apple_dnssd_AppleDNSSD_ConstructName; + Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName; + Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex; + Java_com_apple_dnssd_AppleDNSSD_InitLibrary; + Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord; + Java_com_apple_dnssd_AppleDomainEnum_BeginEnum; + Java_com_apple_dnssd_AppleQuery_CreateQuery; + Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection; + Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord; + Java_com_apple_dnssd_AppleRegistration_AddRecord; + Java_com_apple_dnssd_AppleRegistration_BeginRegister; + Java_com_apple_dnssd_AppleResolver_CreateResolver; + Java_com_apple_dnssd_AppleService_BlockForData; + Java_com_apple_dnssd_AppleService_HaltOperation; + Java_com_apple_dnssd_AppleService_ProcessResults; + local: + *; +}; diff --git a/usr/src/lib/libdns_sd/java/i386/Makefile b/usr/src/lib/libdns_sd/java/i386/Makefile new file mode 100644 index 0000000000..3b6c2a1943 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/i386/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +.KEEP_STATE: + +CPPFLAGS += -_gcc=-Wno-pointer-to-int-cast -_gcc=-Wno-int-to-pointer-cast + +all: $(LIBS) + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libdns_sd/java/sparc/Makefile b/usr/src/lib/libdns_sd/java/sparc/Makefile new file mode 100644 index 0000000000..3b6c2a1943 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/sparc/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +.KEEP_STATE: + +CPPFLAGS += -_gcc=-Wno-pointer-to-int-cast -_gcc=-Wno-int-to-pointer-cast + +all: $(LIBS) + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libdns_sd/java/sparcv9/Makefile b/usr/src/lib/libdns_sd/java/sparcv9/Makefile new file mode 100644 index 0000000000..b645e1caed --- /dev/null +++ b/usr/src/lib/libdns_sd/java/sparcv9/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../../Makefile.lib.64 + +all: $(LIBS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libdns_sd/sparc/Makefile b/usr/src/lib/libdns_sd/sparc/Makefile new file mode 100644 index 0000000000..9c1be5bf3a --- /dev/null +++ b/usr/src/lib/libdns_sd/sparc/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libdns_sd/sparcv9/Makefile b/usr/src/lib/libdns_sd/sparcv9/Makefile new file mode 100644 index 0000000000..abae92c693 --- /dev/null +++ b/usr/src/lib/libdns_sd/sparcv9/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../Makefile.lib.64 + +CFLAGS64 += -erroff=E_ASSIGNMENT_TYPE_MISMATCH + +.KEEP_STATE: + +all: $(LIBS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt index 2dc8835ffd..f9a27370ba 100644 --- a/usr/src/lib/libsecdb/auth_attr.txt +++ b/usr/src/lib/libsecdb/auth_attr.txt @@ -116,6 +116,7 @@ solaris.smf.manage.idmap:::Manage Identity Mapping Service States::help=SmfIdmap solaris.smf.manage.inetd:::Manage inetd and inetd managed services States::help=SmfIntedStates.html solaris.smf.manage.ipsec:::Manage IPsec Service States::help=SmfIPsecStates.html solaris.smf.manage.labels:::Manage label server::help=LabelServer.html +solaris.smf.manage.mdns:::Manage Multicast DNS Service States::help=SmfMDNSStates.html solaris.smf.manage.name-service-cache:::Manage Name Service Cache Daemon Service States::help=SmfNscdStates.html solaris.smf.manage.nwam:::Manage Network Auto-Magic Service States::help=SmfNWAMStates.html solaris.smf.manage.power:::Manage Power Management Service States::help=SmfPowerStates.html @@ -132,6 +133,7 @@ solaris.smf.value.:::Change Values of SMF Service Properties::help=SmfValueHeade solaris.smf.value.idmap:::Change Values of SMF Identity Mapping Service Properties::help=SmfValueIdmap.html solaris.smf.value.inetd:::Change values of SMF Inetd configuration paramaters::help=SmfValueInted.html solaris.smf.value.ipsec:::Change Values of SMF IPsec Properties::help=SmfValueIPsec.html +solaris.smf.value.mdns:::Change Values of MDNS Service Properties::help=SmfValueMDNS.html solaris.smf.value.nwam:::Change Values of SMF Network Auto-Magic Properties::help=SmfValueNWAM.html solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html solaris.smf.value.tnd:::Change Trusted Network Daemon Service Property Values::help=ValueTND.html diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile index 9548660950..906c321caf 100644 --- a/usr/src/lib/libsecdb/help/auths/Makefile +++ b/usr/src/lib/libsecdb/help/auths/Makefile @@ -71,6 +71,7 @@ HTMLENTS = \ SmfInetdStates.html \ SmfIPsecStates.html \ SmfManageHeader.html \ + SmfMDNSStates.html \ SmfModifyAppl.html \ SmfModifyDepend.html \ SmfModifyFramework.html \ @@ -86,6 +87,7 @@ HTMLENTS = \ SmfValueHeader.html \ SmfValueInetd.html \ SmfValueIPsec.html \ + SmfValueMDNS.html \ SmfValueNWAM.html \ SmfValueRouting.html \ SmfWpaStates.html \ diff --git a/usr/src/lib/libsecdb/help/auths/SmfMDNSStates.html b/usr/src/lib/libsecdb/help/auths/SmfMDNSStates.html new file mode 100644 index 0000000000..c3acac86e5 --- /dev/null +++ b/usr/src/lib/libsecdb/help/auths/SmfMDNSStates.html @@ -0,0 +1,40 @@ +<HTML> +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + +Copyright 2007 Sun Microsystems, Inc. All rights reserved. +Use is subject to license terms. +--> +<!-- SCCS keyword +#ident "%Z%%M% %I% %E% SMI" +--> +<!-- + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> +--> +<BODY> +When Manage MDNS Service States is in the Authorizations Include +column, it grants the authorization to enable, disable, or restart +Network Auto-Magic (NWAM) services. +<p> +If Manage MDNS Service States is grayed, then you are not entitled +to Add or Remove this authorization. +<BR> +</BODY> +</HTML> diff --git a/usr/src/lib/libsecdb/help/auths/SmfValueMDNS.html b/usr/src/lib/libsecdb/help/auths/SmfValueMDNS.html new file mode 100644 index 0000000000..9cf3cd0916 --- /dev/null +++ b/usr/src/lib/libsecdb/help/auths/SmfValueMDNS.html @@ -0,0 +1,40 @@ +<HTML> +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + +Copyright 2007 Sun Microsystems, Inc. All rights reserved. +Use is subject to license terms. +--> +<!-- SCCS keyword +#ident "%Z%%M% %I% %E% SMI" +--> +<!-- + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> +--> +<BODY> +When Value MDNS Properties is in the Authorizations Include +column, it grants the the authorization to change Multicast +DNS (mdns) service property values. +<P> +If Value MDNS Properties is grayed, then you are not entitled to +Add or Remove this authorization. +<BR> +</BODY> +</HTML> diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt index bd715a415a..7df27db207 100644 --- a/usr/src/lib/libsecdb/prof_attr.txt +++ b/usr/src/lib/libsecdb/prof_attr.txt @@ -52,7 +52,7 @@ Mail Management:::Manage sendmail & queues:auths=solaris.smf.manage.sendmail;hel Maintenance and Repair:::Maintain and repair a system:auths=solaris.smf.manage.system-log,solaris.label.range;help=RtMaintAndRepair.html Media Backup:::Backup files and file systems:help=RtMediaBkup.html Media Restore:::Restore files and file systems from backups:help=RtMediaRestore.html -Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa;profiles=Network Wifi Management,Inetd Management;help=RtNetMngmnt.html +Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns;profiles=Network Wifi Management,Inetd Management;help=RtNetMngmnt.html Network Security:::Manage network and host security:auths=solaris.smf.manage.ssh,solaris.smf.value.tnd;profiles=Network Wifi Security,Network Link Security,Network IPsec Management;help=RtNetSecure.html Network Wifi Management:::Manage wifi network configuration:auths=solaris.network.wifi.config;help=RtNetWifiMngmnt.html Network Wifi Security:::Manage wifi network security:auths=solaris.network.wifi.wep;help=RtNetWifiSecure.html diff --git a/usr/src/lib/nsswitch/Makefile b/usr/src/lib/nsswitch/Makefile index 44c6081481..dda261d132 100644 --- a/usr/src/lib/nsswitch/Makefile +++ b/usr/src/lib/nsswitch/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -22,7 +21,7 @@ # #ident "%Z%%M% %I% %E% SMI" # -# Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # lib/nsswitch/Makefile @@ -30,7 +29,7 @@ include $(SRC)/Makefile.master -SUBDIRS= files nis nisplus compat dns ldap user +SUBDIRS= files nis nisplus compat dns ldap user mdns all:= TARGET= all clean:= TARGET= clean diff --git a/usr/src/lib/nsswitch/mdns/Makefile b/usr/src/lib/nsswitch/mdns/Makefile new file mode 100644 index 0000000000..f67704e53a --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/Makefile @@ -0,0 +1,45 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/nsswitch/mdns/Makefile + +include ../../../Makefile.master + +FILES_SUBDIRS= $(MACH) $(BUILD64) $(MACH64) + +all:= TARGET= all +clean:= TARGET= clean +clobber:= TARGET= clobber +install:= TARGET= install +lint:= TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint debugmod: $(FILES_SUBDIRS) + +$(MACH) $(MACH64): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/nsswitch/mdns/Makefile.com b/usr/src/lib/nsswitch/mdns/Makefile.com new file mode 100644 index 0000000000..00e335d732 --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/Makefile.com @@ -0,0 +1,42 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/nsswitch/mdns/Makefile.com + +LIBRARY = libnss_mdns.a +VERS = .1 + +OBJECTS = gethostent.o \ + gethostent6.o \ + mdns_common.o + +# include common nsswitch library definitions. +include ../../Makefile.com + +C99MODE = $(C99_ENABLE) +LDLIBS += -lnsl -ldns_sd -lscf +DYNLIB1 = nss_mdns.so$(VERS) + +lint := LDLIBS = $(LDLIBS.lib) -lc -lnsl -lscf -ldns_sd diff --git a/usr/src/lib/nsswitch/mdns/amd64/Makefile b/usr/src/lib/nsswitch/mdns/amd64/Makefile new file mode 100644 index 0000000000..7237707552 --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/amd64/Makefile @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +LIBS = $(DYNLIB1) + +include ../../Makefile.targ + +install: all $(ROOT64DYNLIB) diff --git a/usr/src/lib/nsswitch/mdns/common/gethostent.c b/usr/src/lib/nsswitch/mdns/common/gethostent.c new file mode 100644 index 0000000000..6b5756f0e9 --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/common/gethostent.c @@ -0,0 +1,153 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mdns_common.h" + +/* + * gethostby* functions for the hosts database. The hosts + * database stores IPv4 addresses only. + * mDNS query functions to perform the host lookup + * are in mdns/common/mdns_common.c file. + * _nss_mdns_hosts_constr is called to initialize + * the nsswitch backend data structures. + */ + +static nss_status_t +getbyname(be, a) + mdns_backend_ptr_t be; + void *a; +{ + struct mdns_querydata qdata; + char *hname; + + (void) memset(&qdata, 0, sizeof (struct mdns_querydata)); + + qdata.argp = (nss_XbyY_args_t *)a; + hname = (char *)qdata.argp->key.name; + + _nss_mdns_updatecfg(be); + return (_nss_mdns_querybyname(be, hname, AF_INET, &qdata)); +} + +/*ARGSUSED*/ +static nss_status_t +getbyaddr(be, a) + mdns_backend_ptr_t be; + void *a; +{ + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + struct in_addr addr; + struct mdns_querydata qdata; + char buffer[sizeof ("255.255.255.255.in-addr.arpa.")]; + uint8_t *p; + + (void) memset(&qdata, 0, sizeof (struct mdns_querydata)); + qdata.argp = argp; + + argp->h_errno = 0; + if ((argp->key.hostaddr.type != AF_INET) || + (argp->key.hostaddr.len != sizeof (addr))) + return (NSS_NOTFOUND); + + (void) memcpy(&addr, argp->key.hostaddr.addr, sizeof (addr)); + + if (inet_ntop(AF_INET, (void *) &addr.s_addr, + (void *)qdata.paddrbuf, + sizeof (qdata.paddrbuf)) == NULL) + return (NSS_NOTFOUND); + + qdata.af = AF_INET; + p = (uint8_t *)&addr.s_addr; + (void) snprintf(buffer, sizeof (buffer), + "%u.%u.%u.%u.in-addr.arpa.", p[3], p[2], p[1], p[0]); + + _nss_mdns_updatecfg(be); + return (_nss_mdns_querybyaddr(be, buffer, qdata.af, &qdata)); +} + +/*ARGSUSED*/ +static nss_status_t +_nss_mdns_getent(be, args) + mdns_backend_ptr_t be; + void *args; +{ + return (NSS_UNAVAIL); +} + +/*ARGSUSED*/ +static nss_status_t +_nss_mdns_setent(be, dummy) + mdns_backend_ptr_t be; + void *dummy; +{ + return (NSS_UNAVAIL); +} + +/*ARGSUSED*/ +static nss_status_t +_nss_mdns_endent(be, dummy) + mdns_backend_ptr_t be; + void *dummy; +{ + return (NSS_UNAVAIL); +} + +/*ARGSUSED*/ +static nss_status_t +_nss_mdns_hosts_destr(be, dummy) + mdns_backend_ptr_t be; + void *dummy; +{ + _nss_mdns_destr(be); + return (NSS_SUCCESS); +} + +static mdns_backend_op_t host_ops[] = { + _nss_mdns_hosts_destr, + _nss_mdns_endent, + _nss_mdns_setent, + _nss_mdns_getent, + getbyname, + getbyaddr, +}; + +/*ARGSUSED*/ +nss_backend_t * +_nss_mdns_hosts_constr(dummy1, dummy2, dummy3) + const char *dummy1, *dummy2, *dummy3; +{ + return (_nss_mdns_constr(host_ops, + sizeof (host_ops) / sizeof (host_ops[0]))); +} + +/*ARGSUSED*/ +nss_status_t +_nss_get_mdns_hosts_name(mdns_backend_ptr_t *be, void **bufp, size_t *sizep) +{ + return (_nss_mdns_gethost_withttl(*bufp, *sizep, 0)); +} diff --git a/usr/src/lib/nsswitch/mdns/common/gethostent6.c b/usr/src/lib/nsswitch/mdns/common/gethostent6.c new file mode 100644 index 0000000000..e4288de82b --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/common/gethostent6.c @@ -0,0 +1,174 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mdns_common.h" + +/* + * gethostby* functions for the ipnodes database. The ipnodes + * database stores both IPv4 and IPv6 address information. + * mDNS query functions to perform the host lookup are + * in mdns/common/mdns_common.c file. + * _nss_mdns_ipnodes_constr is called to initialize + * the nsswitch backend data structures. + */ + +static nss_status_t +getbyname(be, a) + mdns_backend_ptr_t be; + void *a; +{ + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + int af = argp->key.ipnode.af_family; + char *hname = (char *)argp->key.ipnode.name; + struct mdns_querydata qdata; + + (void) memset(&qdata, 0, sizeof (struct mdns_querydata)); + qdata.argp = argp; + + _nss_mdns_updatecfg(be); + return (_nss_mdns_querybyname(be, hname, af, &qdata)); +} + +static nss_status_t +getbyaddr(be, a) + mdns_backend_ptr_t be; + void *a; +{ + int i; + char ch; + char *chptr; + uint8_t *p; + struct in6_addr *addr; + struct mdns_querydata qdata; + char addrqryname[ sizeof ("f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f") + \ + sizeof (".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.ip6.arpa.")]; + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + + (void) memset(&qdata, 0, sizeof (struct mdns_querydata)); + qdata.argp = argp; + argp->h_errno = 0; + + if ((argp->key.hostaddr.type != AF_INET6) || + (argp->key.hostaddr.len != sizeof (*addr))) + return (NSS_NOTFOUND); + + /* LINTED E_BAD_PTR_CAST_ALIGN */ + addr = (struct in6_addr *)(argp->key.hostaddr.addr); + + if (IN6_IS_ADDR_V4MAPPED(addr)) { + struct in_addr ipv4addr; + + IN6_V4MAPPED_TO_INADDR(addr, &ipv4addr); + if (inet_ntop(AF_INET, (void *) &ipv4addr.s_addr, + (void *)qdata.paddrbuf, sizeof (qdata.paddrbuf)) == NULL) + return (NSS_NOTFOUND); + qdata.af = AF_INET; + p = (uint8_t *)&ipv4addr.s_addr; + (void) snprintf(addrqryname, sizeof (addrqryname), + "%u.%u.%u.%u.in-addr.arpa.", p[3], p[2], p[1], p[0]); + } else { + if (inet_ntop(AF_INET6, (void *)addr, + (void *)qdata.paddrbuf, + sizeof (qdata.paddrbuf)) == NULL) + return (NSS_NOTFOUND); + qdata.af = AF_INET6; + chptr = addrqryname; + for (i = 0; i < 16; i++) + { + ch = ((char *)addr)[15-i]; + chptr += snprintf(chptr, sizeof (addrqryname)-i*4, + "%X.%X.", ch&0x0f, (ch>>4)&0x0f); + } + (void) strlcpy(chptr, "ip6.arpa.", sizeof (addrqryname)-64); + } + + _nss_mdns_updatecfg(be); + return (_nss_mdns_querybyaddr(be, addrqryname, qdata.af, &qdata)); +} + +/*ARGSUSED*/ +static nss_status_t +_nss_mdns_getent(be, args) + mdns_backend_ptr_t be; + void *args; +{ + return (NSS_UNAVAIL); +} + +/*ARGSUSED*/ +static nss_status_t +_nss_mdns_setent(be, dummy) + mdns_backend_ptr_t be; + void *dummy; +{ + return (NSS_UNAVAIL); +} + +/*ARGSUSED*/ +static nss_status_t +_nss_mdns_endent(be, dummy) + mdns_backend_ptr_t be; + void *dummy; +{ + return (NSS_UNAVAIL); +} + +/*ARGSUSED*/ +static nss_status_t +_nss_mdns_ipnodes_destr(be, dummy) + mdns_backend_ptr_t be; + void *dummy; +{ + _nss_mdns_destr(be); + return (NSS_SUCCESS); +} + +static mdns_backend_op_t ipnodes_ops[] = { + _nss_mdns_ipnodes_destr, + _nss_mdns_endent, + _nss_mdns_setent, + _nss_mdns_getent, + getbyname, + getbyaddr, +}; + +/*ARGSUSED*/ +nss_backend_t * +_nss_mdns_ipnodes_constr(dummy1, dummy2, dummy3) + const char *dummy1, *dummy2, *dummy3; +{ + return (_nss_mdns_constr(ipnodes_ops, + sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0]))); +} + +/*ARGSUSED*/ +nss_status_t +_nss_get_mdns_ipnodes_name(mdns_backend_ptr_t *be, void **bufp, size_t *sizep) +{ + return (_nss_mdns_gethost_withttl(*bufp, *sizep, 1)); +} diff --git a/usr/src/lib/nsswitch/mdns/common/mapfile-vers b/usr/src/lib/nsswitch/mdns/common/mapfile-vers new file mode 100644 index 0000000000..cc7767771a --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/common/mapfile-vers @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# Generic interface definition for usr/src/lib/nsswitch/mdns. + +SUNWprivate_1.1 { + global: + _nss_mdns_hosts_constr; + _nss_mdns_ipnodes_constr; + _nss_get_mdns_hosts_name; + _nss_get_mdns_ipnodes_name; + local: + *; +}; diff --git a/usr/src/lib/nsswitch/mdns/common/mdns_common.c b/usr/src/lib/nsswitch/mdns/common/mdns_common.c new file mode 100644 index 0000000000..3e914e5744 --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/common/mdns_common.c @@ -0,0 +1,763 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "mdns_common.h" + +static int _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype, + DNSServiceQueryRecordReply callback, + struct mdns_querydata *data); +static void _nss_mdns_get_svcstatetimestamp(struct timeval *); +static void _nss_mdns_loadsmfcfg(mdns_backend_ptr_t); +static void _nss_mdns_freesmfcfg(mdns_backend_ptr_t); +static boolean_t cmpdmn(char *, char **, int); +static char *RDataToName(char *data, char *buffer, int datalen, int buflen); +static int searchdomain(mdns_backend_ptr_t, char *, int, char **); +static boolean_t validdomain(mdns_backend_ptr_t, char *, int); + +/* + * This file includes the functions to query for host name + * information via Multicast DNS (mDNS). The function + * _nss_mdns_queryrecord queries for the host information via + * Multicast DNS. _nss_mdns_querybyname and _nss_mdns_querybyaddr + * query for host IP address and hostname by querying for A/AAAA + * and PTR DNS resource records respectively. DNSServiceQueryRecord + * in libdns_sd sends a request to the mDNS daemon (mdnsd) to place + * the DNS query via multicast and return the results. + * mdnsd is managed by SMF (FMRI: svc:/network/dns/multicast:default). + * + * gethostent.c and gethostent6.c implement the nsswitch 'hosts' + * backend module getXbyY functions: getbyname and getbyaddr. + * getby* functions in gethostent.c supports only IPv4 and + * getby* functions in gethostent6.c returns both IPv4 and + * IPv6 results. Functions in gethostent.c and gethostent6.c + * call the _nss_mdns_queryby* functions in mdns_common.c to + * query for host information via mDNS. + * + * Configuration for mdns is stored in SMF and is accessed using + * the FMRI: svc:/network/dns/multicast:default. Configuration + * includes the list of valid DNS domains checked before querying host + * information via mDNS and the search list to use for host lookup via + * mDNS. The default valid domain list in the mDNS service supports host + * lookups for hostnames in the ".local" domain and hostname queries + * for link-local IPv4 and IPv6 addresses. _nss_mdns_loadsmfcfg + * loads the nss_mdns configuration from SMF and the function + * _nss_mdns_updatecfg checks for any updates in nss_mdns configuration. + */ + +static int +_nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype, + DNSServiceQueryRecordReply callback, + struct mdns_querydata *data) +{ + int sockfd; + int flags = kDNSServiceFlagsForceMulticast; /* Multicast only */ + int opinterface = kDNSServiceInterfaceIndexAny; + DNSServiceErrorType err; + DNSServiceRef ref = NULL; + int ret; + struct fd_set readfds; + struct timeval tv; + + data->status = NSS_NOTFOUND; +#ifdef DEBUG + syslog(LOG_DEBUG, "nss_mdns: query called rrname:%s rrtype:%d", + rrname, rrtype); +#endif + err = DNSServiceQueryRecord(&ref, flags, opinterface, + rrname, rrtype, rrclass, callback, data); + if (err != kDNSServiceErr_NoError || ref == NULL || + (sockfd = DNSServiceRefSockFD(ref)) == NULL) { + DNSServiceRefDeallocate(ref); + data->status = NSS_UNAVAIL; + return (NSS_UNAVAIL); + } + + do { + FD_ZERO(&readfds); + FD_SET(sockfd, &readfds); + tv.tv_sec = NSSMDNS_MAXQRYTMO; + tv.tv_usec = 0; + + /* Wait until response received from mDNS daemon */ + ret = select(sockfd + 1, &readfds, NULL, NULL, &tv); + if (!((ret > 0) && FD_ISSET(sockfd, &readfds) && + (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError))) { + data->status = NSS_NOTFOUND; + if (errno != EINTR) + data->qrydone = B_TRUE; + } + } while (data->qrydone != B_TRUE); + + if (data->status == NSS_SUCCESS && (data->withttlbuffer == NULL)) { + nss_XbyY_args_t *argp = data->argp; + if (argp->buf.result != NULL) { + int stat; + + if (data->buffer == NULL) { + data->status = NSS_NOTFOUND; + DNSServiceRefDeallocate(ref); + return (data->status); + } + stat = (*argp->str2ent)(data->buffer, + strlen(data->buffer), + argp->buf.result, argp->buf.buffer, + argp->buf.buflen); + if (stat == NSS_STR_PARSE_SUCCESS) { + argp->returnval = argp->buf.result; + argp->returnlen = 1; + } else { + data->status = NSS_NOTFOUND; + if (stat == NSS_STR_PARSE_ERANGE) + argp->erange = 1; + } + free(data->buffer); + } else { + argp->returnval = argp->buf.buffer; + argp->returnlen = strlen(argp->buf.buffer); + } + data->buffer = NULL; + data->buflen = 0; + } + + if (data->status != NSS_SUCCESS) + data->argp->h_errno = HOST_NOT_FOUND; + + DNSServiceRefDeallocate(ref); + return (data->status); +} + +static void +/* LINTED E_FUNC_ARG_UNUSED */ +_nss_mdns_querynamereply(DNSServiceRef sdRef, const DNSServiceFlags flags, + /* LINTED E_FUNC_ARG_UNUSED */ + uint32_t ifIndex, DNSServiceErrorType errorCode, + const char *fullname, uint16_t rrtype, uint16_t rrclass, + /* LINTED E_FUNC_ARG_UNUSED */ + uint16_t rdlen, const void *rdata, uint32_t ttl, + void *context) +{ + struct mdns_querydata *qdata; + nss_XbyY_args_t *argp; + int firstent = 0; + int af; + char addrstore[INET6_ADDRSTRLEN]; + char *buffer; + int len; + int remlen; + + qdata = (struct mdns_querydata *)context; + argp = qdata->argp; + + if (errorCode != kDNSServiceErr_NoError) { + qdata->qrydone = B_TRUE; + return; + } + if ((flags & kDNSServiceFlagsMoreComing)) + qdata->qrydone = B_FALSE; + else + qdata->qrydone = B_TRUE; + if (!(flags & kDNSServiceFlagsAdd)) + return; + if (rrclass != kDNSServiceClass_IN) + return; + + if (rrtype == kDNSServiceType_A) + af = AF_INET; + else if (rrtype == kDNSServiceType_AAAA) + af = AF_INET6; + else + return; + + if (qdata->buffer == NULL) { + if (qdata->withttlbsize > 0) { + remlen = qdata->buflen = + qdata->withttlbsize; + buffer = qdata->buffer = + qdata->withttlbuffer; + (void) memset(qdata->buffer, 0, remlen); + } else { + remlen = qdata->buflen = + argp->buf.buflen; + if (argp->buf.result != NULL) { + buffer = qdata->buffer = + calloc(1, remlen); + } else { + /* Return in file format */ + (void) memset(argp->buf.buffer, + 0, remlen); + buffer = qdata->buffer = argp->buf.buffer; + } + } + firstent = 1; + } else { + buffer = qdata->buffer + strlen(qdata->buffer); + remlen = qdata->buflen - strlen(qdata->buffer); + } + +#ifdef DEBUG + syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen); +#endif + if (inet_ntop(af, rdata, addrstore, INET6_ADDRSTRLEN) != NULL) { + if (firstent) + len = snprintf(buffer, remlen, "%s %s", + addrstore, fullname); + else + len = snprintf(buffer, remlen, "\n%s %s", + addrstore, fullname); + if (len >= remlen || len < 0) { + qdata->status = NSS_NOTFOUND; + qdata->argp->erange = 1; + qdata->argp->h_errno = HOST_NOT_FOUND; + return; + } + qdata->ttl = ttl; + qdata->status = NSS_SUCCESS; +#ifdef DEBUG + syslog(LOG_DEBUG, "nss_mdns: querynamereply buffer:%s", buffer); +#endif + } else { + qdata->status = NSS_NOTFOUND; + qdata->argp->h_errno = HOST_NOT_FOUND; + } +} + +int +_nss_mdns_querybyname(mdns_backend_ptr_t be, char *qname, + int af, struct mdns_querydata *data) +{ + int rrtype; + int rrclass; + int srchidx = 0; + int rc; + char hname[MAXDNAME]; + char *name; + char *sname; + + rrclass = kDNSServiceClass_IN; + if (af == AF_INET6) + rrtype = kDNSServiceType_ANY; + else if (af == AF_INET) + rrtype = kDNSServiceType_A; + else + return (NSS_NOTFOUND); + + name = strdup(qname); + if (name == NULL) + return (NSS_UNAVAIL); + + while ((srchidx = searchdomain(be, name, srchidx, &sname)) != -1) { + if (sname != NULL) + (void) snprintf(hname, sizeof (hname), "%s.%s", + name, sname); + else + (void) strlcpy(hname, name, sizeof (hname)); +#ifdef DEBUG + syslog(LOG_DEBUG, "nss_mdns: querybyname called" \ + " srchidx:%d af:%d hname:%s", srchidx, af, qname); +#endif + rc = _nss_mdns_queryrecord(hname, rrclass, rrtype, + _nss_mdns_querynamereply, data); + if ((rc == NSS_UNAVAIL) || (rc == NSS_SUCCESS)) { + free(name); + return (rc); + } + } + free(name); + return (NSS_NOTFOUND); +} + +static void +/* LINTED E_FUNC_ARG_UNUSED */ +_nss_mdns_queryaddrreply(DNSServiceRef sdRef, const DNSServiceFlags flags, + /* LINTED E_FUNC_ARG_UNUSED */ + uint32_t ifIndex, DNSServiceErrorType errorCode, + /* LINTED E_FUNC_ARG_UNUSED */ + const char *fullname, uint16_t rrtype, uint16_t rrclass, + uint16_t rdlen, const void *rdata, uint32_t ttl, + void *context) +{ + struct mdns_querydata *qdata; + nss_XbyY_args_t *argp; + char hostname[NI_MAXHOST]; + int firstent = 0; + char *buffer; + int len; + int remlen; + + qdata = (struct mdns_querydata *)context; + argp = qdata->argp; + + if (errorCode != kDNSServiceErr_NoError) { + qdata->qrydone = B_TRUE; + return; + } + if ((flags & kDNSServiceFlagsMoreComing)) + qdata->qrydone = B_FALSE; + else + qdata->qrydone = B_TRUE; + if (!(flags & kDNSServiceFlagsAdd)) + return; + if (rrclass != kDNSServiceClass_IN) + return; + if (rrtype != kDNSServiceType_PTR) + return; + + if (qdata->buffer == NULL) { + remlen = qdata->buflen = argp->buf.buflen; + if (argp->buf.result != NULL) { + buffer = qdata->buffer = calloc(1, remlen); + } else { + /* Return in file format */ + (void) memset(argp->buf.buffer, 0, remlen); + buffer = qdata->buffer = argp->buf.buffer; + } + firstent = 1; + } else { + buffer = qdata->buffer + strlen(qdata->buffer); + remlen = qdata->buflen - strlen(qdata->buffer); + } + + if (RDataToName((char *)rdata, hostname, rdlen, NI_MAXHOST) == NULL) { + qdata->status = NSS_NOTFOUND; + qdata->argp->h_errno = HOST_NOT_FOUND; + return; + } + +#ifdef DEBUG + syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen); +#endif + if (firstent) + len = snprintf(buffer, remlen, "%s %s", + qdata->paddrbuf, hostname); + else + len = snprintf(buffer, remlen, "\n%s %s", + qdata->paddrbuf, hostname); + if (len >= remlen || len < 0) { + qdata->status = NSS_NOTFOUND; + qdata->argp->erange = 1; + qdata->argp->h_errno = HOST_NOT_FOUND; + return; + } + qdata->status = NSS_SUCCESS; + qdata->ttl = ttl; +} + +int +/* LINTED E_FUNC_ARG_UNUSED */ +_nss_mdns_querybyaddr(mdns_backend_ptr_t be, char *name, int af, + struct mdns_querydata *data) +{ + int rrtype; + int rrclass; + +#ifdef DEBUG + syslog(LOG_DEBUG, "nss_mdns: querybyaddr called" \ + " af:%d addr:%s", af, name); +#endif + rrclass = kDNSServiceClass_IN; + rrtype = kDNSServiceType_PTR; + + if (validdomain(be, name, 0) == B_FALSE) { + data->status = NSS_NOTFOUND; + return (NSS_NOTFOUND); + } + return (_nss_mdns_queryrecord(name, rrclass, rrtype, + _nss_mdns_queryaddrreply, data)); +} + +/* + * Converts the encoded name in RData returned + * by mDNS query to name in file format + */ +static char * +RDataToName(char *data, char *buffer, int datalen, int buflen) +{ + char *src = data; + char *srcend = data + datalen; + char *ptr = buffer; + char *end; + char *bend = buffer + buflen - 1; /* terminal '\0' */ + int domainlen = 0; + + while ((src < srcend) && (*src != 0)) { + + /* first byte is len */ + domainlen = *src++; + end = src + domainlen; + + while ((src < end) && (ptr < bend)) { + uint8_t ch = *src++; + if (ch == '.' || ch == '\\') { + *ptr++ = '\\'; + } + *ptr++ = ch; + } + + /* + * Check if we copied entire domain str. and + * if space is still remaining for '.' seperator + */ + if ((src != end) || (ptr == bend)) + return (NULL); + *ptr++ = '.'; + } + *ptr = '\0'; + return (ptr); +} + +nss_backend_t * +_nss_mdns_constr(mdns_backend_op_t ops[], int n_ops) +{ + mdns_backend_ptr_t be; + + if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL) + return (NULL); + be->ops = ops; + be->n_ops = n_ops; + _nss_mdns_updatecfg(be); + return ((nss_backend_t *)be); +} + +void +_nss_mdns_destr(mdns_backend_ptr_t be) +{ + if (be != NULL) { + _nss_mdns_freesmfcfg(be); + free(be); + } +} + +static int +searchdomain(mdns_backend_ptr_t be, char *name, int srchidx, char **sname) +{ + int trailing_dot = 0; + char *ch; + *sname = NULL; + + ch = name + strlen(name) - 1; + if ((*ch) == '.') + trailing_dot++; + + if (trailing_dot && srchidx > 0) + /* + * If there is a trailing dot in the query + * name, do not perform any additional queries + * with search domains. + */ + return (-1); + + if (srchidx == 0) { + /* + * If there is a trailing dot in the query + * or atleast one dot in the query name then + * perform a query as-is once first. + */ + ++srchidx; + if ((trailing_dot || (strchr(name, '.') != NULL))) { + if (validdomain(be, name, 1) == B_TRUE) + return (srchidx); + else if (trailing_dot) + return (-1); + } + } + + if ((srchidx > NSSMDNS_MAXSRCHDMNS) || + (be->dmnsrchlist[srchidx-1] == NULL)) + return (-1); + + *sname = be->dmnsrchlist[srchidx-1]; + return (++srchidx); +} + +/* + * This function determines if the domain name in the query + * matches any of the valid & search domains in the nss_mdns + * configuration. + */ +static boolean_t +validdomain(mdns_backend_ptr_t be, char *name, int chksrchdmns) +{ + char *nameptr; + + /* Remove any trailing and leading dots in the name */ + nameptr = name + strlen(name) - 1; + while (*nameptr && (nameptr != name) && (*nameptr == '.')) + nameptr--; + *(++nameptr) = '\0'; + nameptr = name; + while (*nameptr && (*nameptr == '.')) + nameptr++; + if (*nameptr == '\0') + return (B_FALSE); + + /* Compare with search domains */ + if (chksrchdmns && (cmpdmn(nameptr, be->dmnsrchlist, + NSSMDNS_MAXSRCHDMNS) == B_TRUE)) + return (B_TRUE); + + /* Compare with valid domains */ + return (cmpdmn(nameptr, be->validdmnlist, NSSMDNS_MAXVALIDDMNS)); +} + +static boolean_t +cmpdmn(char *name, char **dmnlist, int maxdmns) +{ + char *vptr; + int vdlen; + char *cptr; + int nlen; + int i; + + nlen = strlen(name); + for (i = 0; (i < maxdmns) && + ((vptr = dmnlist[i]) != NULL); i++) { + vdlen = strlen(vptr); + if (vdlen > nlen) + continue; + cptr = name + nlen - vdlen; + if (strncasecmp(cptr, vptr, vdlen) == 0) + return (B_TRUE); + } + return (B_FALSE); +} + +static void +_nss_mdns_get_svcstatetimestamp(struct timeval *ptv) +{ + scf_handle_t *h; + scf_simple_prop_t *sprop; + int32_t nsec; + + (void) memset(ptv, 0, sizeof (struct timeval)); + + h = scf_handle_create(SCF_VERSION); + if (h == NULL) + return; + + if (scf_handle_bind(h) == -1) { + scf_handle_destroy(h); + return; + } + + if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI, + SCF_PG_RESTARTER, SCF_PROPERTY_STATE_TIMESTAMP)) != NULL) { + ptv->tv_sec = *(time_t *)(scf_simple_prop_next_time(sprop, + &nsec)); + ptv->tv_usec = nsec / 1000; + scf_simple_prop_free(sprop); + } + + if (h != NULL) + scf_handle_destroy(h); +} + +void +_nss_mdns_updatecfg(mdns_backend_ptr_t be) +{ + struct timeval statetimestamp; + + /* + * Update configuration if current svc state timestamp + * is different from last known svc state timestamp + */ + _nss_mdns_get_svcstatetimestamp(&statetimestamp); + if ((statetimestamp.tv_sec == 0) && (statetimestamp.tv_usec == 0)) { + syslog(LOG_ERR, "nss_mdns: error checking " \ + "svc:/network/dns/multicast:default" \ + " service timestamp"); + } else if ((be->conftimestamp.tv_sec == statetimestamp.tv_sec) && + (be->conftimestamp.tv_usec == statetimestamp.tv_usec)) { + return; + } + + _nss_mdns_freesmfcfg(be); + _nss_mdns_loadsmfcfg(be); + be->conftimestamp.tv_sec = statetimestamp.tv_sec; + be->conftimestamp.tv_usec = statetimestamp.tv_usec; +} + +static void +load_mdns_domaincfg(scf_handle_t *h, char **storelist, + const char *scfprop, int maxprops) +{ + scf_simple_prop_t *sprop; + char *tchr; + char *pchr; + int tlen; + int cnt = 0; + + if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI, + SMF_NSSMDNSCFG_PROPGRP, scfprop)) == NULL) + return; + + while ((cnt < maxprops) && + (tchr = scf_simple_prop_next_astring(sprop)) != NULL) { + + /* Remove beginning & trailing '.' chars */ + while (*tchr && (*tchr == '.')) + tchr++; + + if (*tchr && ((tlen = strlen(tchr)) < MAXDNAME)) { + pchr = &tchr[tlen-1]; + while ((pchr != tchr) && (*pchr == '.')) + pchr--; + *(++pchr) = '\0'; + storelist[cnt] = strdup(tchr); + cnt++; + } + } + scf_simple_prop_free(sprop); +} + +static void +_nss_mdns_loadsmfcfg(mdns_backend_ptr_t be) +{ + scf_handle_t *h; + + h = scf_handle_create(SCF_VERSION); + if (h == NULL) + return; + + if (scf_handle_bind(h) == -1) { + scf_handle_destroy(h); + return; + } + + load_mdns_domaincfg(h, &(be->dmnsrchlist[0]), + SMF_NSSMDNSCFG_SRCHPROP, NSSMDNS_MAXSRCHDMNS); + + load_mdns_domaincfg(h, &(be->validdmnlist[0]), + SMF_NSSMDNSCFG_DMNPROP, NSSMDNS_MAXVALIDDMNS); + + if (h != NULL) + scf_handle_destroy(h); +} + +static void +_nss_mdns_freesmfcfg(mdns_backend_ptr_t be) +{ + int idx; + if (be == NULL) + return; + for (idx = 0; idx < NSSMDNS_MAXSRCHDMNS; idx++) { + if (be->dmnsrchlist[idx] != NULL) { + free(be->dmnsrchlist[idx]); + be->dmnsrchlist[idx] = NULL; + } + } + for (idx = 0; idx < NSSMDNS_MAXVALIDDMNS; idx++) { + if (be->validdmnlist[idx] != NULL) { + free(be->validdmnlist[idx]); + be->validdmnlist[idx] = NULL; + } + } +} + +/* + * Performs lookup for IP address by hostname via mDNS and returns + * results along with the TTL value from the mDNS resource records. + * Called by nscd wth a ptr to packed bufer and packed buffer size. + */ +nss_status_t +_nss_mdns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) +{ + nss_pheader_t *pbuf = (nss_pheader_t *)buffer; + nss_XbyY_args_t arg; + int dbop; + int af; + int len; + int blen; + char *dbname; + nss_status_t sret; + char *hname; + struct mdns_querydata qdata; + nssuint_t *pttl; + mdns_backend_ptr_t be = NULL; + + (void) memset(&qdata, 0, sizeof (struct mdns_querydata)); + + qdata.argp = &arg; + + /* + * Retrieve withttl buffer and size from the passed packed buffer. + * Results are returned along with ttl in this buffer. + */ + qdata.withttlbsize = pbuf->data_len - sizeof (nssuint_t); + qdata.withttlbuffer = (char *)buffer + pbuf->data_off; + + sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg); + if (sret != NSS_SUCCESS) + return (NSS_ERROR); + + if (ipnode) { + if (arg.key.ipnode.flags != 0) + return (NSS_ERROR); + hname = (char *)arg.key.ipnode.name; + af = arg.key.ipnode.af_family; + } else { + af = AF_INET; + hname = (char *)arg.key.name; + } + + if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL) + return (NSS_ERROR); + _nss_mdns_updatecfg(be); + + /* Zero out the withttl buffer prior to use */ + (void) memset(qdata.withttlbuffer, 0, qdata.withttlbsize); + +#ifdef DEBUG + syslog(LOG_DEBUG, "nss_mdns: querybyname withttl called" \ + " af:%d hname:%s", af, hname); +#endif + if (_nss_mdns_querybyname(be, hname, af, &qdata) == NSS_SUCCESS) { + blen = strlen(qdata.buffer); + len = ROUND_UP(blen, sizeof (nssuint_t)); + + if (len + sizeof (nssuint_t) > pbuf->data_len) { + _nss_mdns_freesmfcfg(be); + free(be); + return (NSS_ERROR); + } + + pbuf->ext_off = pbuf->data_off + len; + pbuf->ext_len = sizeof (nssuint_t); + pbuf->data_len = blen; + + /* Return ttl in the packed buffer at ext_off */ + pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off)); + *pttl = qdata.ttl; + + _nss_mdns_freesmfcfg(be); + free(be); + return (NSS_SUCCESS); + } + _nss_mdns_freesmfcfg(be); + free(be); + return (NSS_ERROR); +} diff --git a/usr/src/lib/nsswitch/mdns/common/mdns_common.h b/usr/src/lib/nsswitch/mdns/common/mdns_common.h new file mode 100644 index 0000000000..0170b0fe18 --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/common/mdns_common.h @@ -0,0 +1,107 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _MDNS_COMMON_H +#define _MDNS_COMMON_H + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <strings.h> +#include <syslog.h> +#include <sys/time.h> +#include <sys/select.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <nsswitch.h> +#include <nss_common.h> +#include <nss_dbdefs.h> +#include <stdlib.h> +#include <signal.h> +#include <libscf.h> +#include <dns_sd.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define NSSMDNS_MAXQRYTMO 1 +#define NSSMDNS_MAXSRCHDMNS 6 +#define NSSMDNS_MAXVALIDDMNS 10 + +#define SMF_MDNS_FMRI "svc:/network/dns/multicast:default" +#define SMF_NSSMDNSCFG_PROPGRP "nss_mdns_config" +#define SMF_NSSMDNSCFG_SRCHPROP "search" +#define SMF_NSSMDNSCFG_DMNPROP "domain" + +typedef struct mdns_backend *mdns_backend_ptr_t; +typedef nss_status_t (*mdns_backend_op_t)(mdns_backend_ptr_t, void *); + +struct mdns_backend { + mdns_backend_op_t *ops; + nss_dbop_t n_ops; + char *dmnsrchlist[NSSMDNS_MAXSRCHDMNS]; + char *validdmnlist[NSSMDNS_MAXVALIDDMNS]; + struct timeval conftimestamp; +}; + +struct mdns_querydata { + nss_XbyY_args_t *argp; + char *buffer; + int buflen; + int ttl; + boolean_t qrydone; + int status; + int af; + char paddrbuf[INET6_ADDRSTRLEN + 1]; + int withttlbsize; + char *withttlbuffer; +}; + +nss_backend_t *_nss_mdns_constr(mdns_backend_op_t *, int); +void _nss_mdns_destr(mdns_backend_ptr_t); +int _nss_mdns_querybyname(mdns_backend_ptr_t, char *name, + int af, struct mdns_querydata *data); +int _nss_mdns_querybyaddr(mdns_backend_ptr_t, char *name, + int af, struct mdns_querydata *data); +void _nss_mdns_updatecfg(mdns_backend_ptr_t); + +nss_status_t _nss_mdns_gethost_withttl(void *buf, size_t bufsize, int ipnode); +nss_status_t _nss_get_mdns_hosts_name(mdns_backend_ptr_t *be, + void **bufp, size_t *sizep); +nss_status_t _nss_get_mdns_ipnodes_name(mdns_backend_ptr_t *be, + void **bufp, size_t *sizep); + +#ifdef __cplusplus +} +#endif + +#endif /* _MDNS_COMMON_H */ diff --git a/usr/src/lib/nsswitch/mdns/i386/Makefile b/usr/src/lib/nsswitch/mdns/i386/Makefile new file mode 100644 index 0000000000..a250b9553d --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/i386/Makefile @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/nsswitch/mdns/i386/Makefile + +include ../Makefile.com + +LIBS = $(DYNLIB1) + +include ../../Makefile.targ + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/nsswitch/mdns/sparc/Makefile b/usr/src/lib/nsswitch/mdns/sparc/Makefile new file mode 100644 index 0000000000..8fa0a071cb --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/sparc/Makefile @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/nsswitch/mdns/sparc/Makefile + +include ../Makefile.com + +LIBS = $(DYNLIB1) + +include ../../Makefile.targ + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/nsswitch/mdns/sparcv9/Makefile b/usr/src/lib/nsswitch/mdns/sparcv9/Makefile new file mode 100644 index 0000000000..ca03048942 --- /dev/null +++ b/usr/src/lib/nsswitch/mdns/sparcv9/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/nsswitch/mdns/sparcv9/Makefile + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +LIBS = $(DYNLIB1) + +include ../../Makefile.targ + +install: all $(ROOT64DYNLIB) diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile index c58e7b553d..f3d97d538d 100644 --- a/usr/src/pkgdefs/Makefile +++ b/usr/src/pkgdefs/Makefile @@ -195,6 +195,8 @@ COMMON_SUBDIRS= \ SUNWdhcsu \ SUNWdmgtr \ SUNWdmgtu \ + SUNWdsdr \ + SUNWdsdu \ SUNWdoc \ SUNWdpl \ SUNWdtrc \ diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com index bb97b7cb47..ce503fdc43 100644 --- a/usr/src/pkgdefs/SUNW0on/prototype_com +++ b/usr/src/pkgdefs/SUNW0on/prototype_com @@ -240,6 +240,7 @@ f none usr/lib/help/auths/locale/SmfCronStates.html 444 root bin f none usr/lib/help/auths/locale/SmfHeader.html 444 root bin f none usr/lib/help/auths/locale/SmfInetdStates.html 444 root bin f none usr/lib/help/auths/locale/SmfManageHeader.html 444 root bin +f none usr/lib/help/auths/locale/SmfMDNSStates.html 444 root bin f none usr/lib/help/auths/locale/SmfModifyAppl.html 444 root bin f none usr/lib/help/auths/locale/SmfModifyDepend.html 444 root bin f none usr/lib/help/auths/locale/SmfModifyFramework.html 444 root bin @@ -256,6 +257,7 @@ f none usr/lib/help/auths/locale/SmfSyslogStates.html 444 root bin f none usr/lib/help/auths/locale/SmfValueHeader.html 444 root bin f none usr/lib/help/auths/locale/SmfValueInetd.html 444 root bin f none usr/lib/help/auths/locale/SmfValueIPsec.html 444 root bin +f none usr/lib/help/auths/locale/SmfValueMDNS.html 444 root bin f none usr/lib/help/auths/locale/SmfValueNWAM.html 444 root bin f none usr/lib/help/auths/locale/SmfValueRouting.html 444 root bin f none usr/lib/help/auths/locale/SmfWpaStates.html 444 root bin diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com index ebaecf2e1d..4e154ce0bc 100644 --- a/usr/src/pkgdefs/SUNWcsu/prototype_com +++ b/usr/src/pkgdefs/SUNWcsu/prototype_com @@ -478,6 +478,7 @@ f none usr/lib/help/auths/locale/C/SmfAutofsStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfCronStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfHeader.html 444 root bin f none usr/lib/help/auths/locale/C/SmfManageHeader.html 444 root bin +f none usr/lib/help/auths/locale/C/SmfMDNSStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfModifyAppl.html 444 root bin f none usr/lib/help/auths/locale/C/SmfModifyDepend.html 444 root bin f none usr/lib/help/auths/locale/C/SmfModifyFramework.html 444 root bin @@ -495,6 +496,7 @@ f none usr/lib/help/auths/locale/C/SmfSyslogStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueHeader.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueInetd.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueIPsec.html 444 root bin +f none usr/lib/help/auths/locale/C/SmfValueMDNS.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueNWAM.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueRouting.html 444 root bin f none usr/lib/help/auths/locale/C/SmfWpaStates.html 444 root bin diff --git a/usr/src/pkgdefs/SUNWdsdr/Makefile b/usr/src/pkgdefs/SUNWdsdr/Makefile new file mode 100644 index 0000000000..0398b50af2 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdr/Makefile @@ -0,0 +1,37 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +DATAFILES= i.manifest r.manifest + +.KEEP_STATE: + +all: $(FILES) depend + +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWdsdr/depend b/usr/src/pkgdefs/SUNWdsdr/depend new file mode 100644 index 0000000000..e4e4bd8ce0 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdr/depend @@ -0,0 +1,54 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +# +# This package information file defines software dependencies associated +# with the pkg. You can define three types of pkg dependencies with this file: +# P indicates a prerequisite for installation +# I indicates an incompatible package +# R indicates a reverse dependency +# <pkg.abbr> see pkginfo(4), PKG parameter +# <name> see pkginfo(4), NAME parameter +# <version> see pkginfo(4), VERSION parameter +# <arch> see pkginfo(4), ARCH parameter +# <type> <pkg.abbr> <name> +# (<arch>)<version> +# (<arch>)<version> +# ... +# <type> <pkg.abbr> <name> +# ... +# + +P SUNWcar Core Architecture, (Root) +P SUNWcakr Core Solaris Kernel Architecture (Root) +P SUNWkvm Core Architecture, (Kvm) +P SUNWcsr Core Solaris, (Root) +P SUNWckr Core Solaris Kernel (Root) +P SUNWcnetr Core Solaris Network Infrastructure (Root) +P SUNWcsu Core Solaris, (Usr) +P SUNWcslr Core Solaris Libraries (Root) +P SUNWcsd Core Solaris Devices +P SUNWdsdu Multicast DNS and Service Discovery (Usr) diff --git a/usr/src/pkgdefs/SUNWdsdr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWdsdr/pkginfo.tmpl new file mode 100644 index 0000000000..f1c7c71f41 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdr/pkginfo.tmpl @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWdsdr" +NAME="Multicast DNS and Service Discovery (Root)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="root" +MAXINST="1000" +CATEGORY="system" +DESC="Root components for Multicast DNS daemon and service discovery support" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none manifest" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="false" +SUNW_PKG_THISZONE="false" diff --git a/usr/src/pkgdefs/SUNWdsdr/prototype_com b/usr/src/pkgdefs/SUNWdsdr/prototype_com new file mode 100644 index 0000000000..88d9bf9ad8 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdr/prototype_com @@ -0,0 +1,41 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +# packaging files +i depend +i pkginfo +i i.manifest +i r.manifest + +# +# SUNWdsdr +# +d none var 0755 root sys +d none var/svc 0755 root sys +d none var/svc/manifest 0755 root sys +d none var/svc/manifest/network 0755 root sys +d none var/svc/manifest/network/dns 0755 root sys +f manifest var/svc/manifest/network/dns/multicast.xml 0444 root sys diff --git a/usr/src/pkgdefs/SUNWdsdr/prototype_i386 b/usr/src/pkgdefs/SUNWdsdr/prototype_i386 new file mode 100644 index 0000000000..bbc6e3681b --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdr/prototype_i386 @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +!include prototype_com diff --git a/usr/src/pkgdefs/SUNWdsdr/prototype_sparc b/usr/src/pkgdefs/SUNWdsdr/prototype_sparc new file mode 100644 index 0000000000..bbc6e3681b --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdr/prototype_sparc @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +!include prototype_com diff --git a/usr/src/pkgdefs/SUNWdsdu/Makefile b/usr/src/pkgdefs/SUNWdsdu/Makefile new file mode 100644 index 0000000000..b02d335b05 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdu/Makefile @@ -0,0 +1,37 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +DATAFILES= + +.KEEP_STATE: + +all: $(FILES) depend + +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWdsdu/copyright b/usr/src/pkgdefs/SUNWdsdu/copyright new file mode 100644 index 0000000000..c64647a000 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdu/copyright @@ -0,0 +1,43 @@ +#ident "%Z%%M% %I% %E% SMI" + +Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright 2007 Sun Microsystems, Inc. All rights reserved. +Use is subject to license terms. diff --git a/usr/src/pkgdefs/SUNWdsdu/depend b/usr/src/pkgdefs/SUNWdsdu/depend new file mode 100644 index 0000000000..e4aa905021 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdu/depend @@ -0,0 +1,53 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +# +# This package information file defines software dependencies associated +# with the pkg. You can define three types of pkg dependencies with this file: +# P indicates a prerequisite for installation +# I indicates an incompatible package +# R indicates a reverse dependency +# <pkg.abbr> see pkginfo(4), PKG parameter +# <name> see pkginfo(4), NAME parameter +# <version> see pkginfo(4), VERSION parameter +# <arch> see pkginfo(4), ARCH parameter +# <type> <pkg.abbr> <name> +# (<arch>)<version> +# (<arch>)<version> +# ... +# <type> <pkg.abbr> <name> +# ... +# + +P SUNWcar Core Architecture, (Root) +P SUNWcakr Core Solaris Kernel Architecture (Root) +P SUNWkvm Core Architecture, (Kvm) +P SUNWcsr Core Solaris, (Root) +P SUNWckr Core Solaris Kernel (Root) +P SUNWcnetr Core Solaris Network Infrastructure (Root) +P SUNWcsu Core Solaris, (Usr) +P SUNWcslr Core Solaris Libraries (Root) +P SUNWcsd Core Solaris Devices diff --git a/usr/src/pkgdefs/SUNWdsdu/pkginfo.tmpl b/usr/src/pkgdefs/SUNWdsdu/pkginfo.tmpl new file mode 100644 index 0000000000..25d81c188c --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdu/pkginfo.tmpl @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWdsdu" +NAME="Multicast DNS and Service Discovery (Usr)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="usr" +MAXINST="1000" +CATEGORY="system" +DESC="Multicast DNS daemon and service discovery modules" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="false" +SUNW_PKG_THISZONE="false" diff --git a/usr/src/pkgdefs/SUNWdsdu/prototype_com b/usr/src/pkgdefs/SUNWdsdu/prototype_com new file mode 100644 index 0000000000..46f38dc5f7 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdu/prototype_com @@ -0,0 +1,55 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +# packaging files +i copyright +i depend +i pkginfo + +# +# SUNWdsdu +# +d none usr 755 root sys +d none usr/bin 755 root bin +f none usr/bin/dns-sd 0555 root bin +d none usr/include 755 root bin +f none usr/include/dns_sd.h 644 root bin +d none usr/lib 755 root bin +d none usr/lib/inet 755 root bin +f none usr/lib/inet/mdnsd 0555 root bin +f none usr/lib/libdns_sd.so.1 755 root bin +s none usr/lib/libdns_sd.so=libdns_sd.so.1 +f none usr/lib/libjdns_sd.so.1 755 root bin +s none usr/lib/libjdns_sd.so=libjdns_sd.so.1 +f none usr/lib/llib-ldns_sd 644 root bin +f none usr/lib/llib-ldns_sd.ln 644 root bin +f none usr/lib/nss_mdns.so.1 755 root bin +d none usr/share 755 root sys +d none usr/share/lib 755 root sys +d none usr/share/lib/java 755 root sys +f none usr/share/lib/java/dnssd.jar 644 root sys + +!include prototype_doc diff --git a/usr/src/pkgdefs/SUNWdsdu/prototype_doc b/usr/src/pkgdefs/SUNWdsdu/prototype_doc new file mode 100644 index 0000000000..4bba668e41 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdu/prototype_doc @@ -0,0 +1,66 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +#ident "%Z%%M% %I% %E% SMI" + +d none usr/share/lib/java/javadoc 0755 root other +d none usr/share/lib/java/javadoc/dnssd 0755 root other +d none usr/share/lib/java/javadoc/dnssd/api 0755 root other +f none usr/share/lib/java/javadoc/dnssd/api/allclasses-frame.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/allclasses-noframe.html 0644 root other +d none usr/share/lib/java/javadoc/dnssd/api/com 0755 root other +d none usr/share/lib/java/javadoc/dnssd/api/com/apple 0755 root other +d none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd 0755 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/BaseListener.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/BrowseListener.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSD.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSDException.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSDRecordRegistrar.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSDRegistration.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSDService.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DomainListener.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/package-frame.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/package-summary.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/package-tree.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/QueryListener.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/RegisterListener.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/RegisterRecordListener.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/ResolveListener.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/TXTRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/constant-values.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/deprecated-list.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/help-doc.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/index-all.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/index.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/overview-tree.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/package-list 0644 root other +d none usr/share/lib/java/javadoc/dnssd/api/resources 0755 root other +f none usr/share/lib/java/javadoc/dnssd/api/resources/inherit.gif 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/serialized-form.html 0644 root other +f none usr/share/lib/java/javadoc/dnssd/api/stylesheet.css 0644 root other +d none usr/share/lib/java/javadoc/dnssd/examples 0755 root other +f none usr/share/lib/java/javadoc/dnssd/examples/BrowserApp.jar 0644 root sys +f none usr/share/lib/java/javadoc/dnssd/examples/SimpleChat.jar 0644 root sys diff --git a/usr/src/pkgdefs/SUNWdsdu/prototype_i386 b/usr/src/pkgdefs/SUNWdsdu/prototype_i386 new file mode 100644 index 0000000000..6e46b6f421 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdu/prototype_i386 @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +!include prototype_com + +# +d none usr/lib/amd64 755 root bin +f none usr/lib/amd64/libdns_sd.so.1 755 root bin +s none usr/lib/amd64/libdns_sd.so=libdns_sd.so.1 +f none usr/lib/amd64/libjdns_sd.so.1 755 root bin +s none usr/lib/amd64/libjdns_sd.so=libjdns_sd.so.1 +f none usr/lib/amd64/llib-ldns_sd.ln 644 root bin +f none usr/lib/amd64/nss_mdns.so.1 755 root bin diff --git a/usr/src/pkgdefs/SUNWdsdu/prototype_sparc b/usr/src/pkgdefs/SUNWdsdu/prototype_sparc new file mode 100644 index 0000000000..5b12fe85f6 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdsdu/prototype_sparc @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +#ident "%Z%%M% %I% %E% SMI" + +!include prototype_com + +# +d none usr/lib/sparcv9 755 root bin +f none usr/lib/sparcv9/libdns_sd.so.1 755 root bin +s none usr/lib/sparcv9/libdns_sd.so=libdns_sd.so.1 +f none usr/lib/sparcv9/libjdns_sd.so.1 755 root bin +s none usr/lib/sparcv9/libjdns_sd.so=libjdns_sd.so.1 +f none usr/lib/sparcv9/llib-ldns_sd.ln 644 root bin +f none usr/lib/sparcv9/nss_mdns.so.1 755 root bin diff --git a/usr/src/uts/common/netinet/in.h b/usr/src/uts/common/netinet/in.h index 951d4c8653..f46c9753d2 100644 --- a/usr/src/uts/common/netinet/in.h +++ b/usr/src/uts/common/netinet/in.h @@ -197,6 +197,7 @@ typedef uint16_t sa_family_t; #define IPPORT_NAMESERVER 42 #define IPPORT_WHOIS 43 #define IPPORT_DOMAIN 53 +#define IPPORT_MDNS 5353 #define IPPORT_MTP 57 /* |