From 46ac5acdf893c9754cdb317530756794f6eca7f8 Mon Sep 17 00:00:00 2001 From: Ron Pedde Date: Mon, 29 Mar 2004 17:44:44 +0000 Subject: [PATCH] Moving mdns stuff --- src/mdns/Responder.c | 926 ---- src/mdns/mDNS.c | 7031 ------------------------------ src/mdns/mDNSClientAPI.h | 1423 ------ src/mdns/mDNSDebug.h | 115 - src/mdns/mDNSPlatformFunctions.h | 86 - src/mdns/mDNSPosix.c | 1005 ----- src/mdns/mDNSPosix.h | 98 - src/mdns/mDNSUNP.c | 427 -- src/mdns/mDNSUNP.h | 126 - 9 files changed, 11237 deletions(-) delete mode 100755 src/mdns/Responder.c delete mode 100644 src/mdns/mDNS.c delete mode 100644 src/mdns/mDNSClientAPI.h delete mode 100644 src/mdns/mDNSDebug.h delete mode 100644 src/mdns/mDNSPlatformFunctions.h delete mode 100755 src/mdns/mDNSPosix.c delete mode 100755 src/mdns/mDNSPosix.h delete mode 100755 src/mdns/mDNSUNP.c delete mode 100755 src/mdns/mDNSUNP.h diff --git a/src/mdns/Responder.c b/src/mdns/Responder.c deleted file mode 100755 index d683a03b..00000000 --- a/src/mdns/Responder.c +++ /dev/null @@ -1,926 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log$ -Revision 1.2 2004/03/02 00:03:37 rpedde -Merge new rendezvous code - -Revision 1.16 2003/08/14 02:19:55 cheshire - Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord - -Revision 1.15 2003/08/12 19:56:26 cheshire -Update to APSL 2.0 - -Revision 1.14 2003/08/06 18:20:51 cheshire -Makefile cleanup - -Revision 1.13 2003/07/23 00:00:04 cheshire -Add comments - -Revision 1.12 2003/07/15 01:55:16 cheshire - Need to implement service registration with subtypes - -Revision 1.11 2003/07/14 18:11:54 cheshire -Fix stricter compiler warnings - -Revision 1.10 2003/07/10 20:27:31 cheshire - mDNSResponder Posix version is missing a 'b' in the getopt option string - -Revision 1.9 2003/07/02 21:19:59 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.8 2003/06/18 05:48:41 cheshire -Fix warnings - -Revision 1.7 2003/05/06 00:00:50 cheshire - Rationalize naming of domainname manipulation functions - -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 2003/02/20 06:48:36 cheshire -Bug #: 3169535 Xserve RAID needs to do interface-specific registrations -Reviewed by: Josh Graessley, Bob Bradley - -Revision 1.4 2003/01/28 03:07:46 cheshire -Add extra parameter to mDNS_RenameAndReregisterService(), -and add support for specifying a domain other than dot-local. - -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 - -*/ - -#include "mDNSClientAPI.h"// Defines the interface to the client layer above -#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform - -#include -#include // For printf() -#include // For exit() etc. -#include // For strlen() etc. -#include // For select() -#include // For errno, EINTR -#include -#include - -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark ***** Globals -#endif - -static mDNS mDNSStorage; // mDNS core uses this to store its globals -static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals - -static const char *gProgramName = "mDNSResponderPosix"; - -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark ***** Signals -#endif - -static volatile mDNSBool gReceivedSigUsr1; -static volatile mDNSBool gReceivedSigHup; -static volatile mDNSBool gStopNow; - -// We support 4 signals. -// -// o SIGUSR1 toggles verbose mode on and off in debug builds -// o SIGHUP triggers the program to re-read its preferences. -// o SIGINT causes an orderly shutdown of the program. -// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous) -// o SIGKILL kills us dead (easy to implement :-) -// -// There are fatal race conditions in our signal handling, but there's not much -// we can do about them while remaining within the Posix space. Specifically, -// if a signal arrives after we test the globals its sets but before we call -// select, the signal will be dropped. The user will have to send the signal -// again. Unfortunately, Posix does not have a "sigselect" to atomically -// modify the signal mask and start a select. - -static void HandleSigUsr1(int sigraised) - // If we get a SIGUSR1 we toggle the state of the - // verbose mode. -{ - assert(sigraised == SIGUSR1); - gReceivedSigUsr1 = mDNStrue; -} - -static void HandleSigHup(int sigraised) - // A handler for SIGHUP that causes us to break out of the - // main event loop when the user kill 1's us. This has the - // effect of triggered the main loop to deregister the - // current services and re-read the preferences. -{ - assert(sigraised == SIGHUP); - gReceivedSigHup = mDNStrue; -} - -static void HandleSigInt(int sigraised) - // A handler for SIGINT that causes us to break out of the - // main event loop when the user types ^C. This has the - // effect of quitting the program. -{ - assert(sigraised == SIGINT); - - if (gMDNSPlatformPosixVerboseLevel > 0) { - fprintf(stderr, "\nSIGINT\n"); - } - gStopNow = mDNStrue; -} - -static void HandleSigQuit(int sigraised) - // If we get a SIGQUIT the user is desperate and we - // just call mDNS_Close directly. This is definitely - // not safe (because it could reenter mDNS), but - // we presume that the user has already tried the safe - // alternatives. -{ - assert(sigraised == SIGQUIT); - - if (gMDNSPlatformPosixVerboseLevel > 0) { - fprintf(stderr, "\nSIGQUIT\n"); - } - mDNS_Close(&mDNSStorage); - exit(0); -} - -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark ***** Parameter Checking -#endif - -static mDNSBool CheckThatRichTextHostNameIsUsable(const char *richTextHostName, mDNSBool printExplanation) - // Checks that richTextHostName is a reasonable host name - // label and, if it isn't and printExplanation is true, prints - // an explanation of why not. -{ - mDNSBool result; - domainlabel richLabel; - domainlabel poorLabel; - - result = mDNStrue; - if (result && strlen(richTextHostName) > 63) { - if (printExplanation) { - fprintf(stderr, - "%s: Host name is too long (must be 63 characters or less)\n", - gProgramName); - } - result = mDNSfalse; - } - if (result && richTextHostName[0] == 0) { - if (printExplanation) { - fprintf(stderr, "%s: Host name can't be empty\n", gProgramName); - } - result = mDNSfalse; - } - if (result) { - MakeDomainLabelFromLiteralString(&richLabel, richTextHostName); - ConvertUTF8PstringToRFC1034HostLabel(richLabel.c, &poorLabel); - if (poorLabel.c[0] == 0) { - if (printExplanation) { - fprintf(stderr, - "%s: Host name doesn't produce a usable RFC-1034 name\n", - gProgramName); - } - result = mDNSfalse; - } - } - return result; -} - -static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation) - // Checks that serviceType is a reasonable service type - // label and, if it isn't and printExplanation is true, prints - // an explanation of why not. -{ - mDNSBool result; - - result = mDNStrue; - if (result && strlen(serviceType) > 63) { - if (printExplanation) { - fprintf(stderr, - "%s: Service type is too long (must be 63 characters or less)\n", - gProgramName); - } - result = mDNSfalse; - } - if (result && serviceType[0] == 0) { - if (printExplanation) { - fprintf(stderr, - "%s: Service type can't be empty\n", - gProgramName); - } - result = mDNSfalse; - } - return result; -} - -static mDNSBool CheckThatServiceTextIsUsable(const char *serviceText, mDNSBool printExplanation, - mDNSu8 *pStringList, mDNSu16 *pStringListLen) - // Checks that serviceText is a reasonable service text record - // and, if it isn't and printExplanation is true, prints - // an explanation of why not. Also parse the text into - // the packed PString buffer denoted by pStringList and - // return the length of that buffer in *pStringListLen. - // Note that this routine assumes that the buffer is - // sizeof(RDataBody) bytes long. -{ - mDNSBool result; - size_t serviceTextLen; - - // Note that parsing a C string into a PString list always - // expands the data by one character, so the following - // compare is ">=", not ">". Here's the logic: - // - // #1 For a string with not ^A's, the PString length is one - // greater than the C string length because we add a length - // byte. - // #2 For every regular (not ^A) character you add to the C - // string, you add a regular character to the PString list. - // This does not affect the equivalence stated in #1. - // #3 For every ^A you add to the C string, you add a length - // byte to the PString list but you also eliminate the ^A, - // which again does not affect the equivalence stated in #1. - - result = mDNStrue; - serviceTextLen = strlen(serviceText); - if (result && strlen(serviceText) >= sizeof(RDataBody)) { - if (printExplanation) { - fprintf(stderr, - "%s: Service text record is too long (must be less than %d characters)\n", - gProgramName, - (int) sizeof(RDataBody) ); - } - result = mDNSfalse; - } - - // Now break the string up into PStrings delimited by ^A. - // We know the data will fit so we can ignore buffer overrun concerns. - // However, we still have to treat runs long than 255 characters as - // an error. - - if (result) { - int lastPStringOffset; - int i; - int thisPStringLen; - - // This algorithm is a little tricky. We start by copying - // the string directly into the output buffer, shifted up by - // one byte. We then fill in the first byte with a ^A. - // We then walk backwards through the buffer and, for each - // ^A that we find, we replace it with the difference between - // its offset and the offset of the last ^A that we found - // (ie lastPStringOffset). - - memcpy(&pStringList[1], serviceText, serviceTextLen); - pStringList[0] = 1; - lastPStringOffset = serviceTextLen + 1; - for (i = serviceTextLen; i >= 0; i--) { - if ( pStringList[i] == 1 ) { - thisPStringLen = (lastPStringOffset - i - 1); - assert(thisPStringLen >= 0); - if (thisPStringLen > 255) { - result = mDNSfalse; - if (printExplanation) { - fprintf(stderr, - "%s: Each component of the service text record must be 255 characters or less\n", - gProgramName); - } - break; - } else { - pStringList[i] = thisPStringLen; - lastPStringOffset = i; - } - } - } - - *pStringListLen = serviceTextLen + 1; - } - - return result; -} - -static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation) - // Checks that portNumber is a reasonable port number - // and, if it isn't and printExplanation is true, prints - // an explanation of why not. -{ - mDNSBool result; - - result = mDNStrue; - if (result && (portNumber <= 0 || portNumber > 65535)) { - if (printExplanation) { - fprintf(stderr, - "%s: Port number specified by -p must be in range 1..65535\n", - gProgramName); - } - result = mDNSfalse; - } - return result; -} - -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark ***** Command Line Arguments -#endif - -static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid"; -static const char kDefaultServiceType[] = "_afpovertcp._tcp."; -static const char kDefaultServiceDomain[] = "local."; -enum { - kDefaultPortNumber = 548 -}; - -static void PrintUsage() -{ - fprintf(stderr, - "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-x TXT] [-p port] [-f file] [-b] [-P pidfile]\n", - gProgramName); - fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); - fprintf(stderr, " 0 = no debugging info (default)\n"); - fprintf(stderr, " 1 = standard debugging info\n"); - fprintf(stderr, " 2 = intense debugging info\n"); - fprintf(stderr, " can be cycled kill -USR1\n"); - fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n"); - fprintf(stderr, " -n uses 'name' as the host name (default is none)\n"); - fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType); - fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain); - fprintf(stderr, " -x uses 'TXT' as the service TXT record (default is empty)\n"); - fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber); - fprintf(stderr, " -f reads a service list from 'file'\n"); - fprintf(stderr, " -b forces daemon (background) mode\n"); - fprintf(stderr, " -P uses 'pidfile' as the PID file\n"); - fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile); - fprintf(stderr, " only meaningful if -b also specified\n"); -} - -static mDNSBool gAvoidPort53 = mDNStrue; -static const char *gRichTextHostName = ""; -static const char *gServiceType = kDefaultServiceType; -static const char *gServiceDomain = kDefaultServiceDomain; -static mDNSu8 gServiceText[sizeof(RDataBody)]; -static mDNSu16 gServiceTextLen = 0; -static int gPortNumber = kDefaultPortNumber; -static const char *gServiceFile = ""; -static mDNSBool gDaemon = mDNSfalse; -static const char *gPIDFile = kDefaultPIDFile; - -static void ParseArguments(int argc, char **argv) - // Parses our command line arguments into the global variables - // listed above. -{ - int ch; - - // Set gProgramName to the last path component of argv[0] - - gProgramName = strrchr(argv[0], '/'); - if (gProgramName == NULL) { - gProgramName = argv[0]; - } else { - gProgramName += 1; - } - - // Parse command line options using getopt. - - do { - ch = getopt(argc, argv, "v:rn:t:d:x:p:f:dPb"); - if (ch != -1) { - switch (ch) { - case 'v': - gMDNSPlatformPosixVerboseLevel = atoi(optarg); - if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { - fprintf(stderr, - "%s: Verbose mode must be in the range 0..2\n", - gProgramName); - exit(1); - } - break; - case 'r': - gAvoidPort53 = mDNSfalse; - break; - case 'n': - gRichTextHostName = optarg; - if ( ! CheckThatRichTextHostNameIsUsable(gRichTextHostName, mDNStrue) ) { - exit(1); - } - break; - case 't': - gServiceType = optarg; - if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { - exit(1); - } - break; - case 'd': - gServiceDomain = optarg; - break; - case 'x': - if ( ! CheckThatServiceTextIsUsable(optarg, mDNStrue, gServiceText, &gServiceTextLen) ) { - exit(1); - } - break; - case 'p': - gPortNumber = atol(optarg); - if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) { - exit(1); - } - break; - case 'f': - gServiceFile = optarg; - break; - case 'b': - gDaemon = mDNStrue; - break; - case 'P': - gPIDFile = optarg; - break; - case '?': - default: - PrintUsage(); - exit(1); - break; - } - } - } while (ch != -1); - - // Check for any left over command line arguments. - - if (optind != argc) { - PrintUsage(); - fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]); - exit(1); - } - - // Check for inconsistency between the arguments. - - if ( (gRichTextHostName[0] == 0) && (gServiceFile[0] == 0) ) { - PrintUsage(); - fprintf(stderr, "%s: You must specify a service to register (-n) or a service file (-f).\n", gProgramName); - exit(1); - } -} - -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark ***** Registration -#endif - -typedef struct PosixService PosixService; - -struct PosixService { - ServiceRecordSet coreServ; - PosixService *next; - int serviceID; -}; - -static PosixService *gServiceList = NULL; - -static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status) - // mDNS core calls this routine to tell us about the status of - // our registration. The appropriate action to take depends - // entirely on the value of status. -{ - switch (status) { - - case mStatus_NoError: - debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name.c); - // Do nothing; our name was successfully registered. We may - // get more call backs in the future. - break; - - case mStatus_NameConflict: - debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name.c); - - // In the event of a conflict, this sample RegistrationCallback - // just calls mDNS_RenameAndReregisterService to automatically - // pick a new unique name for the service. For a device such as a - // printer, this may be appropriate. For a device with a user - // interface, and a screen, and a keyboard, the appropriate response - // may be to prompt the user and ask them to choose a new name for - // the service. - // - // Also, what do we do if mDNS_RenameAndReregisterService returns an - // error. Right now I have no place to send that error to. - - status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL); - assert(status == mStatus_NoError); - break; - - case mStatus_MemFree: - debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name.c); - - // When debugging is enabled, make sure that thisRegistration - // is not on our gServiceList. - - #if !defined(NDEBUG) - { - PosixService *cursor; - - cursor = gServiceList; - while (cursor != NULL) { - assert(&cursor->coreServ != thisRegistration); - cursor = cursor->next; - } - } - #endif - free(thisRegistration); - break; - - default: - debugf("Callback: %##s Unknown Status %d", thisRegistration->RR_SRV.resrec.name.c, status); - break; - } -} - -static int gServiceID = 0; - -static mStatus RegisterOneService(const char * richTextHostName, - const char * serviceType, - const char * serviceDomain, - const mDNSu8 text[], - mDNSu16 textLen, - long portNumber) -{ - mStatus status; - PosixService * thisServ; - mDNSOpaque16 port; - domainlabel name; - domainname type; - domainname domain; - - status = mStatus_NoError; - thisServ = (PosixService *) malloc(sizeof(*thisServ)); - if (thisServ == NULL) { - status = mStatus_NoMemoryErr; - } - if (status == mStatus_NoError) { - MakeDomainLabelFromLiteralString(&name, richTextHostName); - MakeDomainNameFromDNSNameString(&type, serviceType); - MakeDomainNameFromDNSNameString(&domain, serviceDomain); - port.b[0] = (portNumber >> 8) & 0x0FF; - port.b[1] = (portNumber >> 0) & 0x0FF;; - status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ, - &name, &type, &domain, // Name, type, domain - NULL, port, // Host and port - text, textLen, // TXT data, length - NULL, 0, // Subtypes - mDNSInterface_Any, // Interace ID - RegistrationCallback, thisServ); // Callback and context - } - if (status == mStatus_NoError) { - thisServ->serviceID = gServiceID; - gServiceID += 1; - - thisServ->next = gServiceList; - gServiceList = thisServ; - - if (gMDNSPlatformPosixVerboseLevel > 0) { - fprintf(stderr, - "%s: Registered service %d, name '%s', type '%s', port %ld\n", - gProgramName, - thisServ->serviceID, - richTextHostName, - serviceType, - portNumber); - } - } else { - if (thisServ != NULL) { - free(thisServ); - } - } - return status; -} - -static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp) -{ - mDNSBool good; - size_t len; - - good = (fgets(buf, bufSize, fp) != NULL); - if (good) { - len = strlen(buf); - good = (len > 0 && buf[len - 1] == '\n'); - } - if (good) { - buf[len - 1] = 0; - } - return good; -} - -static mStatus RegisterServicesInFile(const char *filePath) -{ - mStatus status; - FILE * fp; - int junk; - mDNSBool good; - int ch; - char name[256]; - char type[256]; - const char *dom = kDefaultServiceDomain; - char rawText[1024]; - mDNSu8 text[sizeof(RDataBody)]; - mDNSu16 textLen; - char port[256]; - - status = mStatus_NoError; - fp = fopen(filePath, "r"); - if (fp == NULL) { - status = mStatus_UnknownErr; - } - if (status == mStatus_NoError) { - good = mDNStrue; - do { - // Skip over any blank lines. - do { - ch = fgetc(fp); - } while ( ch == '\n' || ch == '\r' ); - if (ch != EOF) { - good = (ungetc(ch, fp) == ch); - } - - // Read three lines, check them for validity, and register the service. - if ( good && ! feof(fp) ) { - good = ReadALine(name, sizeof(name), fp); - if (good) { - good = ReadALine(type, sizeof(type), fp); - } - if (good) { - char *p = type; - while (*p && *p != ' ') p++; - if (*p) { - *p = 0; - dom = p+1; - } - } - if (good) { - good = ReadALine(rawText, sizeof(rawText), fp); - } - if (good) { - good = ReadALine(port, sizeof(port), fp); - } - if (good) { - good = CheckThatRichTextHostNameIsUsable(name, mDNSfalse) - && CheckThatServiceTypeIsUsable(type, mDNSfalse) - && CheckThatServiceTextIsUsable(rawText, mDNSfalse, text, &textLen) - && CheckThatPortNumberIsUsable(atol(port), mDNSfalse); - } - if (good) { - status = RegisterOneService(name, type, dom, text, textLen, atol(port)); - if (status != mStatus_NoError) { - fprintf(stderr, - "%s: Failed to register service, name = %s, type = %s, port = %s\n", - gProgramName, - name, - type, - port); - status = mStatus_NoError; // keep reading - } - } - } - } while (good && !feof(fp)); - - if ( ! good ) { - fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, gServiceFile); - } - } - - if (fp != NULL) { - junk = fclose(fp); - assert(junk == 0); - } - - return status; -} - -static mStatus RegisterOurServices(void) -{ - mStatus status; - - status = mStatus_NoError; - if (gRichTextHostName[0] != 0) { - status = RegisterOneService(gRichTextHostName, - gServiceType, - gServiceDomain, - gServiceText, gServiceTextLen, - gPortNumber); - } - if (status == mStatus_NoError && gServiceFile[0] != 0) { - status = RegisterServicesInFile(gServiceFile); - } - return status; -} - -static void DeregisterOurServices(void) -{ - PosixService *thisServ; - int thisServID; - - while (gServiceList != NULL) { - thisServ = gServiceList; - gServiceList = thisServ->next; - - thisServID = thisServ->serviceID; - - mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ); - - if (gMDNSPlatformPosixVerboseLevel > 0) { - fprintf(stderr, - "%s: Deregistered service %d\n", - gProgramName, - thisServ->serviceID); - } - } -} - -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark **** Main -#endif - -#ifdef NOT_HAVE_DAEMON - - // The version of Solaris that I tested on didn't have the daemon - // call. This implementation was basically stolen from the - // Mac OS X standard C library. - - static int daemon(int nochdir, int noclose) - { - int fd; - - switch (fork()) { - case -1: - return (-1); - case 0: - break; - default: - _exit(0); - } - - if (setsid() == -1) - return (-1); - - if (!nochdir) - (void)chdir("/"); - - if (!noclose && (fd = _open("/dev/null", O_RDWR, 0)) != -1) { - (void)dup2(fd, STDIN_FILENO); - (void)dup2(fd, STDOUT_FILENO); - (void)dup2(fd, STDERR_FILENO); - if (fd > 2) - (void)_close(fd); - } - return (0); - } - -#endif /* NOT_HAVE_DAEMON */ - -int main(int argc, char **argv) -{ - mStatus status; - int result; - - // Parse our command line arguments. This won't come back if there's an error. - - ParseArguments(argc, argv); - - // If we're told to run as a daemon, then do that straight away. - // Note that we don't treat the inability to create our PID - // file as an error. Also note that we assign getpid to a long - // because printf has no format specified for pid_t. - - if (gDaemon) { - if (gMDNSPlatformPosixVerboseLevel > 0) { - fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName); - } - daemon(0,0); - { - FILE *fp; - int junk; - - fp = fopen(gPIDFile, "w"); - if (fp != NULL) { - fprintf(fp, "%ld\n", (long) getpid()); - junk = fclose(fp); - assert(junk == 0); - } - } - } else { - if (gMDNSPlatformPosixVerboseLevel > 0) { - fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid()); - } - } - - status = mDNS_Init(&mDNSStorage, &PlatformStorage, - mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, - mDNS_Init_AdvertiseLocalAddresses, - mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); - if (status != mStatus_NoError) return(2); - - status = RegisterOurServices(); - if (status != mStatus_NoError) return(2); - - signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP - signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C - signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed) - signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 - - while (!gStopNow) - { - int nfds = 0; - fd_set readfds; - struct timeval timeout; - int result; - - // 1. Set up the fd_set as usual here. - // This example client has no file descriptors of its own, - // but a real application would call FD_SET to add them to the set here - FD_ZERO(&readfds); - - // 2. Set up the timeout. - // This example client has no other work it needs to be doing, - // so we set an effectively infinite timeout - timeout.tv_sec = 0x3FFFFFFF; - timeout.tv_usec = 0; - - // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout - mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); - - // 4. Call select as normal - verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); - result = select(nfds, &readfds, NULL, NULL, &timeout); - - if (result < 0) - { - verbosedebugf("select() returned %d errno %d", result, errno); - if (errno != EINTR) gStopNow = mDNStrue; - else - { - if (gReceivedSigUsr1) - { - gReceivedSigUsr1 = mDNSfalse; - gMDNSPlatformPosixVerboseLevel += 1; - if (gMDNSPlatformPosixVerboseLevel > 2) - gMDNSPlatformPosixVerboseLevel = 0; - if ( gMDNSPlatformPosixVerboseLevel > 0 ) - fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel); - } - if (gReceivedSigHup) - { - if (gMDNSPlatformPosixVerboseLevel > 0) - fprintf(stderr, "\nSIGHUP\n"); - gReceivedSigHup = mDNSfalse; - DeregisterOurServices(); - status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); - if (status != mStatus_NoError) break; - status = RegisterOurServices(); - if (status != mStatus_NoError) break; - } - } - } - else - { - // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work - mDNSPosixProcessFDSet(&mDNSStorage, &readfds); - - // 6. This example client has no other work it needs to be doing, - // but a real client would do its work here - // ... (do work) ... - } - } - - debugf("Exiting"); - - DeregisterOurServices(); - mDNS_Close(&mDNSStorage); - - if (status == mStatus_NoError) { - result = 0; - } else { - result = 2; - } - if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { - fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result); - } - - return result; -} diff --git a/src/mdns/mDNS.c b/src/mdns/mDNS.c deleted file mode 100644 index 9d49b765..00000000 --- a/src/mdns/mDNS.c +++ /dev/null @@ -1,7031 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - * - * 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 , - * 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$ -Revision 1.4 2004/03/02 00:14:26 rpedde -Update to mdns 58.3 - -Revision 1.307.2.2 2003/12/20 01:51:40 cheshire -: 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.307.2.1 2003/12/03 11:20:27 cheshire -: Stop and start of a service uses old ip address (with old port number) - -Revision 1.307 2003/09/09 20:13:30 cheshire - 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 - 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 - Initial probes and queries not grouped on wake-from-sleep - -Revision 1.304 2003/09/09 02:41:19 cheshire - Don't send a Goodbye record if we never announced it - -Revision 1.303 2003/09/05 19:55:02 cheshire - Include address records when announcing SRV records - -Revision 1.302 2003/09/05 00:01:36 cheshire - Don't accelerate queries that have large KA lists - -Revision 1.301 2003/09/04 22:51:13 cheshire - Group probes and goodbyes better - -Revision 1.300 2003/09/03 02:40:37 cheshire - 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 - CacheRecordRmv ERROR -Don't update m->NewQuestions until *after* CheckCacheExpiration(); - -Revision 1.298 2003/09/03 01:47:01 cheshire - Rendezvous 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 - 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 - 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 - Add syslog message to report when query is reset because of immediate answer burst - -Revision 1.294 2003/08/27 02:30:22 cheshire - 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 - Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() - -Revision 1.292 2003/08/21 19:27:36 cheshire - Traffic reduction: No need to announce record for longer than TTL - -Revision 1.291 2003/08/21 18:57:44 cheshire - 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 - Efficiency: Reduce repeated queries - -Revision 1.288 2003/08/20 23:39:30 cheshire - 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 - Cleanup: Review syslog messages - -Revision 1.285 2003/08/20 01:59:06 cheshire - 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 - 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 - 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 - 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 - 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 - mDNSResponder divide by zero in mDNSPlatformTimeNow() - -Revision 1.277 2003/08/18 19:05:44 cheshire - UpdateRecord not working right -Added "newrdlength" field to hold new length of updated rdata - -Revision 1.276 2003/08/16 03:39:00 cheshire - InterfaceID -1 indicates "local only" - -Revision 1.275 2003/08/16 02:51:27 cheshire - 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 - 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 - 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 - Include cache records in SIGINFO output -Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them - -Revision 1.271 2003/08/14 02:17:05 cheshire - Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord - -Revision 1.270 2003/08/13 17:07:28 ksekar -Bug #: : 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 - 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 - Improve efficiency by restricting cases where we have to walk the entire cache - -Revision 1.264 2003/08/09 00:55:02 cheshire - 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 - Remove "Cache size now xxx" messages - -Revision 1.261 2003/08/08 19:18:45 cheshire - Only retrigger questions on platforms with the "PhantomInterfaces" bug - -Revision 1.260 2003/08/08 18:55:48 cheshire - Guard against time going backwards - -Revision 1.259 2003/08/08 18:36:04 cheshire - Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug - -Revision 1.258 2003/08/08 16:22:05 cheshire - Need to check validity of TXT (and other) records -Remove unneeded LogMsg - -Revision 1.257 2003/08/07 01:41:08 cheshire - Ignore packets with invalid source address (all zeroes or all ones) - -Revision 1.256 2003/08/06 23:25:51 cheshire - 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 - 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 - 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 - Need to check IP TTL on responses - -Revision 1.247 2003/08/05 00:56:39 cheshire - 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 - 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 - 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 - ConvertDomainLabelToCString() needs to escape escape characters - -Revision 1.237 2003/07/19 03:23:13 cheshire - 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 - 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 - 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 - Rendezvous 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 - Rate-limit responses, to guard against packet flooding - -Revision 1.228 2003/07/16 20:50:27 cheshire - 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 - 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 - 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 - Need to implement service registration with subtypes - -Revision 1.218 2003/07/14 16:26:06 cheshire - 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 - 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 - 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 - 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 - After name conflict, appended number should be higher than previous number - -Revision 1.210 2003/07/12 01:43:28 cheshire - 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 - 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 - No more local.arpa - -Revision 1.206 2003/07/11 00:45:02 cheshire - 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 - mDNSResponder should log a message after 16 unsuccessful probes - -Revision 1.203 2003/07/10 23:53:41 cheshire - Hostname conflict naming should not use two hyphens - -Revision 1.202 2003/07/04 02:23:20 cheshire - 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 - 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 -: Lots of "have given xxx answers" syslog warnings -Added more detailed debugging information - -Revision 1.198 2003/07/03 22:19:30 cheshire - Bug fix in 3274153 breaks TiVo -Make exception to allow _tivo_servemedia._tcp. - -Revision 1.197 2003/07/02 22:33:05 cheshire - 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 - Update copyright notices, etc., in source code comments - -Revision 1.195 2003/07/02 19:56:58 cheshire - 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 - 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 - Merge of build failure fix for gcc 3.3 - -Revision 1.189 2003/06/11 19:24:03 cheshire - Crash in SendQueries/SendResponses when no active interfaces -Slight refinement to previous checkin - -Revision 1.188 2003/06/10 20:33:28 cheshire - Crash in SendQueries/SendResponses when no active interfaces - -Revision 1.187 2003/06/10 04:30:44 cheshire - 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 - 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 - 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 - 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 - 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 - 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 - 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 - When query produces zero results, call mDNS_Reconfirm() on any antecedent records - -Revision 1.173 2003/06/07 01:22:13 cheshire - mDNSResponder needs an mDNS_Reconfirm() function - -Revision 1.172 2003/06/07 00:59:42 cheshire - 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 - 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 - 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 - 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 - 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 - Duplicate registrations not handled as efficiently as they should be - -Revision 1.159 2003/06/03 03:31:57 cheshire - 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 - Add ability to discover what services are on a network - -Revision 1.156 2003/05/30 23:56:49 cheshire - Crash after error in mDNS_RegisterService() -Need to set "sr->Extras = mDNSNULL" before returning - -Revision 1.155 2003/05/30 23:48:00 cheshire - 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 - 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 - ConstructServiceName needs to be more restrictive - -Revision 1.152 2003/05/29 22:39:16 cheshire - Don't truncate strings in the middle of a UTF-8 character - -Revision 1.151 2003/05/29 06:35:42 cheshire - mDNSCoreReceiveResponse() purging wrong record - -Revision 1.150 2003/05/29 06:25:45 cheshire - Need to call CheckCacheExpiration() *before* AnswerNewQuestion() - -Revision 1.149 2003/05/29 06:18:39 cheshire - Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv - -Revision 1.148 2003/05/29 06:11:34 cheshire - 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 - 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 - 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 - mDNSResponder not sending probes at the prescribed time - -Revision 1.142 2003/05/28 03:13:07 cheshire - mDNSResponder allows invalid service registrations -Require that the transport protocol be _udp or _tcp - -Revision 1.141 2003/05/28 02:19:12 cheshire - 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 - Misleading messages generated by iChat - -Revision 1.138 2003/05/27 22:35:00 cheshire - mDNS_RegisterInterface needs to retrigger questions - -Revision 1.137 2003/05/27 20:04:33 cheshire - mDNSResponder crash in mDNS_vsnprintf() - -Revision 1.136 2003/05/27 18:50:07 cheshire - mDNS_StartResolveService doesn't inform client of port number changes - -Revision 1.135 2003/05/26 04:57:28 cheshire - Delay queries when there are already answers in the cache - -Revision 1.134 2003/05/26 04:54:54 cheshire - 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 - sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead - -Revision 1.131 2003/05/26 00:42:05 cheshire - Temporarily include mDNSResponder version in packets - -Revision 1.130 2003/05/24 16:39:48 cheshire - 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 - After name change, mDNSResponder needs to re-probe for name uniqueness - -Revision 1.127 2003/05/23 01:02:15 ksekar -Bug #: : mDNSResponder needs to include unique id in default name - -Revision 1.126 2003/05/22 02:29:22 cheshire - 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 - 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 -Bug #: 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 - 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 - mDNSResponder should be smarter about reconfigurations -More minor refinements: -CFSocket.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 - 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 - Rendezvous 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 - Also protect against NULL domainlabels - -Revision 1.113 2003/05/07 00:28:18 cheshire - Need to make mDNSResponder more defensive against bad clients - -Revision 1.112 2003/05/06 00:00:46 cheshire - Rationalize naming of domainname manipulation functions - -Revision 1.111 2003/05/05 23:42:08 cheshire - 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 - mDNS_vsnprintf needs to be more defensive against invalid domain names - -Revision 1.109 2003/04/26 02:41:56 cheshire - Change timenow from a local variable to a structure member - -Revision 1.108 2003/04/25 01:45:56 cheshire - mDNS_RegisterNoSuchService needs to include a host name - -Revision 1.107 2003/04/25 00:41:31 cheshire - Create single routine PurgeCacheResourceRecord(), to avoid bugs in future - -Revision 1.106 2003/04/22 03:14:45 cheshire - Include Include instrumented mDNSResponder in panther now - -Revision 1.105 2003/04/22 01:07:43 cheshire - 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 -Bug #: Incorrect goodbye packet after conflict - -Revision 1.102 2003/04/17 03:06:28 cheshire -Bug #: 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 -Bug #: 3229014 -Added a hash to lookup records in the cache. - -Revision 1.100 2003/04/15 18:53:14 cheshire -Bug #: Bug in ScheduleNextTask -mDNS.c 1.94 incorrectly combined two "if" statements into one. - -Revision 1.99 2003/04/15 18:09:13 jgraessl -Bug #: 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 - Off-by-one error in probe rate limiting - -Revision 1.97 2003/04/02 01:48:17 cheshire - 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 - 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 - 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 - 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.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 - 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 3179007 mDNSResponder needs to include AAAA records in additional answer section - -Revision 1.80 2003/02/21 02:47:53 cheshire -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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: -3086540 computer name changes not handled properly -3124348 service name changes are not properly handled -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 -Bug #: 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-existance -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 -*/ - -#define TEST_LOCALONLY_FOR_EVERYTHING 0 - -#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above -#include "mDNSPlatformFunctions.h" // Defines the interface required of the supporting layer below - -// 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 "const object should be initialized" - // We know that static/globals are defined to be zeroed in ANSI C, and to avoid this warning would require some - // *really* ugly chunk of zeroes and curly braces to initialize zeroRR and mDNSprintf_format_default to all zeroes - #pragma warning(disable:4132) - - // 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 - 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; - -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - Program Constants -#endif - -mDNSexport const ResourceRecord zeroRR; -mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; -mDNSexport const mDNSv4Addr zeroIPAddr = { { 0 } }; -mDNSexport const mDNSv6Addr zerov6Addr = { { 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 } }; -mDNSlocal const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; - -mDNSexport const mDNSInterfaceID mDNSInterface_Any = { 0 }; -mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = { (mDNSInterfaceID)~0 }; - -#define UnicastDNSPortAsNumber 53 -#define MulticastDNSPortAsNumber 5353 -mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; -mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; -mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; -mDNSexport const mDNSv4Addr AllDNSLinkGroup = { { 224, 0, 0, 251 } }; -mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } }; -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 } } } }; - -static const mDNSOpaque16 zeroID = { { 0, 0 } }; -static const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; -static const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; -#define zeroDomainNamePtr ((domainname*)"") - -// Any records bigger than this are considered 'large' records -#define SmallRecordLimit 1024 - -#define kDefaultTTLforUnique 240 -#define kDefaultTTLforShared (2*3600) - -#define kMaxUpdateCredits 10 - -static const char *const mDNS_DomainTypeNames[] = - { - "_browse._mdns._udp.local.", - "_default._browse._mdns._udp.local.", - "_register._mdns._udp.local.", - "_default._register._mdns._udp.local." - }; - -#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC))) - -// *************************************************************************** -#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; - -mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) - { - mDNSu32 nwritten = 0; - int c; - buflen--; // Pre-reserve one space in the buffer for the terminating nul - - 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[] = "<>"; s = emsg; i = sizeof(emsg)-1; } - else - { - unsigned short *w = (unsigned short *)a; - s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end - if (F.altForm) - { - mDNSAddr *ip = (mDNSAddr*)a; - a = (unsigned char *)&ip->ip.v4; - w = (unsigned short *)&ip->ip.v6; - switch (ip->type) - { - case mDNSAddrType_IPv4: F.precision = 4; break; - case mDNSAddrType_IPv6: F.precision = 16; 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), "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X", - w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); 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[] = "<>"; s = emsg; i = sizeof(emsg)-1; } - else switch (F.altForm) - { - case 0: { char *a=s; i=0; while(*a++) i++; break; } // C string - 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), "<>", *a); break; } - if (s + *a >= &mDNS_VACB[254]) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<>"); 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; - } - } - if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character - { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } - break; - - case '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), "<>", c); - - case '%' : *sbuffer++ = (char)c; - if (++nwritten >= buflen) goto exit; - break; - } - - if (i < F.fieldWidth && !F.leftJustify) // Pad on the left - do { - *sbuffer++ = ' '; - if (++nwritten >= buflen) goto exit; - } while (i < --F.fieldWidth); - - if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character - { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } - for (j=0; j= 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 - -mDNSexport char *DNSTypeName(mDNSu16 rrtype) - { - switch (rrtype) - { - case kDNSType_A: return("Addr"); - case kDNSType_CNAME:return("CNAME"); - 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); - } - } - } - -mDNSexport char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd) - { - char *ptr = m->MsgBuffer; - mDNSu32 length = mDNS_snprintf(m->MsgBuffer, 79, "%4d %##s %s ", rr->rdlength, rr->name.c, DNSTypeName(rr->rrtype)); - switch (rr->rrtype) - { - case kDNSType_A: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%.4a", &rd->ip); break; - case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->name); break; - case kDNSType_HINFO:// Display this the same as TXT (just show first string) - case kDNSType_TXT: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%#s", rd->txt.c); break; - case kDNSType_AAAA: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%.16a", &rd->ipv6); break; - case kDNSType_SRV: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->srv.target); break; - default: mDNS_snprintf(m->MsgBuffer+length, 79-length, "RDLen %d: %s", - rr->rdlength, rd->data); break; - } - for (ptr = m->MsgBuffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.'; - return(m->MsgBuffer); - } - -mDNSlocal mDNSu32 mDNSRandom(mDNSu32 max) - { - static mDNSu32 seed = 0; - mDNSu32 mask = 1; - - if (!seed) seed = (mDNSu32)mDNSPlatformTimeNow(); - while (mask < max) mask = (mask << 1) | 1; - do seed = seed * 21 + 1; while ((seed & mask) > max); - return (seed & mask); - } - -#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 mDNSIPv4AddressIsZero(A) mDNSSameIPv4Address((A), zeroIPAddr) -#define mDNSIPv6AddressIsZero(A) mDNSSameIPv6Address((A), zerov6Addr) - -#define mDNSIPv4AddressIsOnes(A) mDNSSameIPv4Address((A), onesIPv4Addr) -#define mDNSIPv6AddressIsOnes(A) mDNSSameIPv6Address((A), onesIPv6Addr) - -#define mDNSAddressIsZero(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) - -mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2) - { - if (ip1->type == ip2->type) - { - switch (ip1->type) - { - 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); - } - -mDNSlocal mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) - { - switch(ip->type) - { - case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroup.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); - } - } - -mDNSlocal const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf) - { - while (intf && !intf->InterfaceActive) intf = intf->next; - return(intf); - } - -mDNSlocal mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf) - { - const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); - if (next) return(next->InterfaceID); else return(mDNSNULL); - } - -#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); - } - -// *************************************************************************** -#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)) - -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; ic; - 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); - } - -// 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). -mDNSlocal 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 *cstr) - { - 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 - 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 - { - if (*cstr == '\\' || *cstr == '.') // If a second escape, or a dot, - c = (mDNSu8)*cstr++; // just use the second character - else if (mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1]) && mdnsIsDigit(cstr[2])) - { // else, if three decimal digits, - int v0 = cstr[0] - '0'; // then interpret as three-digit decimal - int v1 = cstr[1] - '0'; - int v2 = cstr[2] - '0'; - int val = v0 * 100 + v1 * 10 + v2; - if (val <= 255) { c = (mDNSu8)val; cstr += 3; } // If valid 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 1005 bytes -// The longest legal domain name is 255 bytes, in the form of three 64-byte labels, one 62-byte label, -// and the null root label. -// If every label character has to be escaped as a four-byte escape sequence, the maximum textual -// ascii display of this is 63*4 + 63*4 + 63*4 + 61*4 = 1000 label characters, -// plus four dots and the null at the end of the C string = 1005 -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 -#define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || mdnsIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') ) - -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) - { - const mDNSu8 *s2 = type->c + 1 + type->c[0]; - if (type->c[0] > 0 && type->c[0] < 0x40 && - s2[0] > 0 && s2[0] < 0x40 && - s2[1+s2[0]] > 0 && s2[1+s2[0]] < 0x40) - { - name = (domainlabel *)type; - type = (domainname *)s2; - } - } - - 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) { errormsg="Invalid service application protocol name"; goto fail; } - if (src[1] != '_') { errormsg="Service 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="Service application protocol name must contain only letters, digits, and hyphens"; goto fail; } - for (i=0; i<=len; i++) *dst++ = *src++; - - len = *src; - //if (len == 0 || len >= 0x40) { errormsg="Invalid service transport protocol name"; goto fail; } - 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="Service 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; - 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); - } - -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 from the domain name - len = *src; - if (len >= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - - dst = type->c; // Extract the service type from the domain name - len = *src; - if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - - len = *src; - if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - *dst++ = 0; // Put the null root label on the end of the service type - - dst = domain->c; // Extract the service domain from the domain name - while (*src) - { - len = *src; - if (len >= 0x40) - { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse); } - if (src + 1 + len + 1 >= max) - { debugf("DeconstructServiceName: 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); - } - -// 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. -mDNSlocal 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. -mDNSlocal 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). -mDNSlocal void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText) - { - mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 3 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++; } - - if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - chars)) - { - name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - chars); - // If the following character is a UTF-8 continuation character, - // we just chopped a multi-byte UTF-8 character in the middle, so strip back to a safe truncation point - while (name->c[0] > 0 && (name->c[name->c[0]+1] & 0xC0) == 0x80) name->c[0]--; - } - - if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; } - else { name->c[++name->c[0]] = '-'; } - - while (divisor) - { - name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor); - val %= divisor; - divisor /= 10; - } - - 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 - -#define RRIsAddressType(RR) ((RR)->resrec.rrtype == kDNSType_A || (RR)->resrec.rrtype == kDNSType_AAAA) -#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 RRUniqueOrKnownUnique(RR) ((RR)->RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeKnownUnique)) - -#define DefaultProbeCountForTypeUnique ((mDNSu8)3) -#define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) - -// For records that have *never* been announced on the wire, their AnnounceCount will be set to InitialAnnounceCount (10). -// When de-registering these records we do not need to send any goodbye packet because we never announced them in the first -// place. If AnnounceCount is less than InitialAnnounceCount that means we have announced them at least once, so a goodbye -// packet is needed. For this reason, if we ever reset AnnounceCount (e.g. after an interface change) we set it to -// ReannounceCount (9), not InitialAnnounceCount. If we were to reset AnnounceCount back to InitialAnnounceCount that would -// imply that the record had never been announced on the wire (which is false) and if the client were then to immediately -// deregister that record before it had a chance to announce, we'd fail to send its goodbye packet (which would be a bug). -#define InitialAnnounceCount ((mDNSu8)10) -#define ReannounceCount ((mDNSu8)9) - -// 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 - -mDNSlocal 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); - if (r1->rdnamehash != r2->rdnamehash) return(mDNSfalse); - switch(r1->rrtype) - { - case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->rdata->u.name)); - - case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->rdata->u.srv.priority && - r1->rdata->u.srv.weight == r2->rdata->u.srv.weight && - r1->rdata->u.srv.port.NotAnInteger == r2->rdata->u.srv.port.NotAnInteger && - SameDomainName(&r1->rdata->u.srv.target, &r2->rdata->u.srv.target) ); - - default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->rdata->u.data, r1->rdlength)); - } - } - -mDNSlocal mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) - { - if (rr->InterfaceID && - q ->InterfaceID && - 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)); - } - -mDNSlocal 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); - } - -#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS) - -mDNSlocal 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); - } - -// 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 the -// authoratative record is in the probing state. Probes are sent with the wildcard type, so a response of -// any type should match, even if it is not 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 != kDNSRecordTypeUnique && 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 mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) - { - RDataBody *rd = &rr->rdata->u; - const domainname *const name = estimate ? &rr->name : mDNSNULL; - switch (rr->rrtype) - { - case kDNSType_A: return(sizeof(rd->ip)); break; - case kDNSType_CNAME:// 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)); break; - case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name)); - default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); - return(rr->rdlength); - } - } - -mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr) - { - if (rr->resrec.RecordType == kDNSRecordTypeUnique) - { - 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); - } - } - -#define GetRRDomainNameTarget(RR) ( \ - ((RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR) ? &(RR)->rdata->u.name : \ - ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL ) - -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 = (m->timenow + DefaultProbeIntervalForTypeUnique) | 1; - // 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); - } - -mDNSlocal 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 = RDataHashValue(rr->rdlength, &rr->rdata->u); - rr->rdnamehash = target ? DomainNameHashValue(target) : 0; - } - -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->hostname)) - debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name.c, target->c); - - if (target && !SameDomainName(target, &m->hostname)) - { - AssignDomainName(*target, m->hostname); - 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->AnnounceCount < InitialAnnounceCount && 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)); - - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); - InitializeLastAPTime(m,rr); - } - } - -mDNSlocal void CompleteProbing(mDNS *const m, AuthRecord *const rr) - { - verbosedebugf("Probing for %##s (%s) complete", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - 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 - } - } - -#define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME) - -mDNSlocal mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd) - { - mDNSu16 len; - switch(rrtype) - { - case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); - - case kDNSType_NS: // Same as PTR - case kDNSType_MD: // Same as PTR - case kDNSType_MF: // Same as PTR - case kDNSType_CNAME:// Same as PTR - //case kDNSType_SOA not checked - case kDNSType_MB: // Same as PTR - case kDNSType_MG: // Same as PTR - case kDNSType_MR: // Same as PTR - //case kDNSType_NULL not checked (no specified format, so always valid) - //case kDNSType_WKS not checked - case kDNSType_PTR: len = 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: { - 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: len = DomainNameLength(&rd->u.mx.exchange); - return(len <= MAX_DOMAIN_NAME && rdlength == 2+len); - - case kDNSType_SRV: len = DomainNameLength(&rd->u.srv.target); - return(len <= MAX_DOMAIN_NAME && rdlength == 6+len); - - default: return(mDNStrue); // Allow all other types without checking - } - } - -// 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; - AuthRecord **l = &m->LocalOnlyRecords; - -#if TEST_LOCALONLY_FOR_EVERYTHING - rr->resrec.InterfaceID = (mDNSInterfaceID)~0; -#endif - - while (*p && *p != rr) p=&(*p)->next; - while (*d && *d != rr) d=&(*d)->next; - while (*l && *l != rr) l=&(*l)->next; - if (*d || *p || *l) - { - 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 != ((mDNSInterfaceID)~0)) - { - 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 - - // Field Group 2: Transient state for Authoritative Records - rr->Acknowledged = mDNSfalse; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - rr->AnnounceCount = InitialAnnounceCount; - rr->IncludeInProbe = mDNSfalse; - rr->ImmedAnswer = mDNSNULL; - rr->ImmedAdditional = mDNSNULL; - rr->SendRNow = mDNSNULL; - rr->v4Requester = zeroIPAddr; - rr->v6Requester = zerov6Addr; - rr->NextResponse = mDNSNULL; - rr->NR_AnswerTo = mDNSNULL; - rr->NR_AdditionalTo = mDNSNULL; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); - 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) - { - if (target) target->c[0] = 0; - SetTargetToHostName(m, rr); // This also sets rdlength and rdestimate for us - } - 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", GetRRDisplayString(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", GetRRDisplayString(m, rr)); return(mStatus_Invalid); } - - rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name); - rr->resrec.rdatahash = RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u); - rr->resrec.rdnamehash = target ? DomainNameHashValue(target) : 0; - - if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) - { - debugf("Adding %p %##s (%s) to LocalOnly list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - *l = rr; - if (!m->NewLocalOnlyRecords) m->NewLocalOnlyRecords = rr; - // 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->LocalOnlyRecords; 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 DiscardLocalOnlyRecords so we'll deliver the callback - { - debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - m->DiscardLocalOnlyRecords = mDNStrue; - } - else // else no conflict, so set ProbeCount to zero and update RecordType as appropriate - { - rr->ProbeCount = 0; - if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; - } - } - } - else - { - // Now that's 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 %p %##s (%s) to duplicate list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - *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 %p %##s (%s) to active record list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - *p = rr; - } - } - return(mStatus_NoError); - } - -mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) - { - m->ProbeFailTime = m->timenow; - m->NumFailedProbes++; - // If we've had ten or more probe failures, rate-limit to one every five seconds - // The result is ORed with 1 to make sure SuppressProbes is not accidentally set to zero - if (m->NumFailedProbes >= 10) m->SuppressProbes = (m->timenow + mDNSPlatformOneSecond * 5) | 1; - if (m->NumFailedProbes >= 16) - LogMsg("Name in use: %##s (%s); need to choose another (%d)", - rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), m->NumFailedProbes); - } - -// 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) - { - mDNSu8 RecordType = rr->resrec.RecordType; - AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records - if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) p = &m->LocalOnlyRecords; - 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 - { - AuthRecord *r2 = m->DuplicateRecords; - while (r2) - { - if (RecordIsLocalDuplicate(r2, rr)) { mDNS_Deregister_internal(m, r2, drt); r2 = m->DuplicateRecords; } - else r2=r2->next; - } - } - 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->ImmedAnswer = rr->ImmedAnswer; - 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; - if (RecordType == kDNSRecordTypeShared) rr->AnnounceCount = InitialAnnounceCount; - } - } - } - 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 && RecordType == kDNSRecordTypeShared) rr->AnnounceCount = InitialAnnounceCount; - 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) - debugf("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->AnnounceCount < InitialAnnounceCount) - { - 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 (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) - m->DiscardLocalOnlyRecords = mDNStrue; - else - { - 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->NewLocalOnlyRecords == rr) m->NewLocalOnlyRecords = 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) - { - 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 - } - - // 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 (RecordType == kDNSRecordTypeShared) - { - if (rr->RecordCallback) - rr->RecordCallback(m, rr, mStatus_MemFree); - } - else if (drt == mDNS_Dereg_conflict) - { - RecordProbeFailure(m, rr); - if (rr->RecordCallback) - rr->RecordCallback(m, rr, mStatus_NameConflict); - } - m->mDNS_reentrancy--; // Decrement to block mDNS API calls again - } - return(mStatus_NoError); - } - -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - -#pragma mark - DNS Message Creation Functions -#endif - -mDNSlocal 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; - } - -mDNSlocal 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 -mDNSlocal mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, - mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name) - { - const mDNSu8 *const base = (const mDNSu8 *const)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 ); - 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; irrtype) - { - 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.ip.b[0]; - *ptr++ = rr->rdata->u.ip.b[1]; - *ptr++ = rr->rdata->u.ip.b[2]; - *ptr++ = rr->rdata->u.ip.b[3]; - return(ptr); - - case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name)); - - case kDNSType_HINFO:// Same as TXT - case kDNSType_TXT: if (ptr + rr->rdlength > limit) return(mDNSNULL); - mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength); - return(ptr + rr->rdlength); - - 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 ); - *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8); - *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight ); - *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)); - - default: if (ptr + rr->rdlength > limit) return(mDNSNULL); - debugf("putRData: Warning! Writing resource type %d as raw data", rr->rrtype); - mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength); - return(ptr + rr->rdlength); - } - } - -mDNSlocal mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl) - { - mDNSu8 *endofrdata; - mDNSu16 actualLength; - const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; - - // 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 - if (msg->h.numAnswers || msg->h.numAuthorities || msg->h.numAdditionals) - limit = msg->data + NormalMaxDNSMessageData; - - 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 ); - ptr[2] = (mDNSu8)(rr->rrclass >> 8); - ptr[3] = (mDNSu8)(rr->rrclass ); - ptr[4] = (mDNSu8)(ttl >> 24); - ptr[5] = (mDNSu8)(ttl >> 16); - ptr[6] = (mDNSu8)(ttl >> 8); - ptr[7] = (mDNSu8)(ttl ); - 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 ); - - (*count)++; - return(endofrdata); - } - -#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) - -mDNSlocal 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)); - } - -#if 0 -mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, - mDNSu16 *count, const AuthRecord *rr) - { - 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->resrec.rrtype >> 8); // Put type - ptr[1] = (mDNSu8)(rr->resrec.rrtype ); - ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class - ptr[3] = (mDNSu8)(rr->resrec.rrclass ); - 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); - } -#endif - -mDNSlocal 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 ); - ptr[2] = (mDNSu8)(rrclass >> 8); - ptr[3] = (mDNSu8)(rrclass ); - msg->h.numQuestions++; - return(ptr+4); - } - -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - DNS Message Parsing Functions -#endif - -mDNSlocal 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. -mDNSlocal 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; ic); - 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); - } - -mDNSlocal 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); - } - -#define GetLargeResourceRecord(m, msg, p, e, i, t, L) \ - (((L)->r.rdatastorage.MaxRDLength = MaximumRDSize), GetResourceRecord((m), (msg), (p), (e), (i), (t), &(L)->r, (RData*)&(L)->r.rdatastorage)) - -mDNSlocal const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, - const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, CacheRecord *rr, RData *RDataStorage) - { - mDNSu16 pktrdlength; - - rr->next = mDNSNULL; - rr->resrec.RecordType = RecordType; - - rr->NextInKAList = mDNSNULL; - rr->TimeRcvd = m->timenow; - rr->NextRequiredQuery = m->timenow; // Will be updated to the real value when we call SetNextCacheCheckTime() - rr->LastUsed = m->timenow; - rr->UseCount = 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) - 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)) - rr->resrec.RecordType |= kDNSRecordTypePacketUniqueMask; - ptr += 10; - if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } - - if (RDataStorage) - rr->resrec.rdata = RDataStorage; - else - { - rr->resrec.rdata = (RData*)&rr->rdatastorage; - rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); - } - - switch (rr->resrec.rrtype) - { - case kDNSType_A: rr->resrec.rdata->u.ip.b[0] = ptr[0]; - rr->resrec.rdata->u.ip.b[1] = ptr[1]; - rr->resrec.rdata->u.ip.b[2] = ptr[2]; - rr->resrec.rdata->u.ip.b[3] = ptr[3]; - break; - - case kDNSType_CNAME:// Same as PTR - 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; - - 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); - - return(ptr + pktrdlength); - } - -mDNSlocal 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); - } - -mDNSlocal 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); - } - -mDNSlocal 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); - } - -mDNSlocal 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); - } - -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - -#pragma mark - Packet Sending Functions -#endif - -mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, mDNSIPPort srcport, const mDNSAddr *dst, mDNSIPPort dstport) - { - mStatus status; - mDNSu16 numQuestions = msg->h.numQuestions; - mDNSu16 numAnswers = msg->h.numAnswers; - mDNSu16 numAuthorities = msg->h.numAuthorities; - mDNSu16 numAdditionals = msg->h.numAdditionals; - - // Put all the integer values in IETF byte-order (MSB first, LSB second) - mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; - *ptr++ = (mDNSu8)(numQuestions >> 8); - *ptr++ = (mDNSu8)(numQuestions ); - *ptr++ = (mDNSu8)(numAnswers >> 8); - *ptr++ = (mDNSu8)(numAnswers ); - *ptr++ = (mDNSu8)(numAuthorities >> 8); - *ptr++ = (mDNSu8)(numAuthorities ); - *ptr++ = (mDNSu8)(numAdditionals >> 8); - *ptr++ = (mDNSu8)(numAdditionals ); - - // Send the packet on the wire - status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, srcport, 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 = numAdditionals; - - return(status); - } - -mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr) - { - // Setting AnnounceCount to InitialAnnounceCount signals mDNS_Deregister_internal() - // that it should go ahead and immediately dispose of this registration - rr->resrec.RecordType = kDNSRecordTypeShared; - rr->AnnounceCount = InitialAnnounceCount; - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - } - -// 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; - m->CurrentRecord = rr->next; - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - CompleteDeregistration(m, rr); - } - } - -mDNSlocal mDNSBool HaveSentEntireRRSet(const mDNS *const m, const AuthRecord *const rr, mDNSInterfaceID InterfaceID) - { - // Try to find another member of this set that we're still planning to send on this interface - const AuthRecord *a; - for (a = m->ResourceRecords; a; a=a->next) - if (a->SendRNow == InterfaceID && a != rr && SameResourceRecordSignature(&a->resrec, &rr->resrec)) break; - return (a == mDNSNULL); // If no more members of this set found, then we should set the cache flush bit - } - -// 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; - - // *** - // *** 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) - { - if (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) - { - if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; - else rr->NextUpdateCredit = (m->timenow + mDNSPlatformOneSecond * 60) | 1; - } - 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 the related Address record(s) - 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 (RRIsAddressType(r2) && // 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.rdnamehash == 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->ImmedAnswer = mDNSInterfaceMark; // ... 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); - } - - // *** - // *** 2. Loop through interface list, sending records as appropriate - // *** - - while (intf) - { - int numDereg = 0; - int numAnnounce = 0; - int numAnswer = 0; - DNSMessage response; - mDNSu8 *responseptr = response.data; - mDNSu8 *newptr; - InitializeDNSMessage(&response.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(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0); - if (!newptr && response.h.numAnswers) break; - numDereg++; - responseptr = newptr; - } - else if (rr->NewRData) // 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" the old data before we replace it. - // We compare with "InitialAnnounceCount-1" instead of "InitialAnnounceCount" because by the time - // we get to this place in this routine we've we've already decremented rr->AnnounceCount - if (ResourceRecordIsValidAnswer(rr) && rr->AnnounceCount < InitialAnnounceCount-1) - { - newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0); - if (!newptr && response.h.numAnswers) break; - numDereg++; - responseptr = newptr; - } - // 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) && HaveSentEntireRRSet(m, rr, intf->InterfaceID)) - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecord(&response, responseptr, &response.h.numAnswers, &rr->resrec); - rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state - if (newptr) responseptr = newptr; - SetNewRData(&rr->resrec, OldRData, oldrdlength); - } - else - { - if ((rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && HaveSentEntireRRSet(m, rr, intf->InterfaceID)) - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecordTTL(&response, responseptr, &response.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 && response.h.numAnswers) break; - 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) - { - // Since additionals are optional, we clear ImmedAdditional anyway, even if we subsequently find it doesn't fit in the packet - rr->ImmedAdditional = mDNSNULL; - if (newptr && ResourceRecordIsValidAnswer(rr)) - { - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - { - // Try to find another member of this set that we're still planning to send on this interface - const AuthRecord *a; - for (a = m->ResourceRecords; a; a=a->next) - if (a->ImmedAdditional == intf->InterfaceID && SameResourceRecordSignature(&a->resrec, &rr->resrec)) break; - if (a == mDNSNULL) // If no more members of this set found - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - } - newptr = PutResourceRecord(&response, newptr, &response.h.numAdditionals, &rr->resrec); - if (newptr) responseptr = newptr; - rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state - } - } - - if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it - { - 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", - response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); - mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v4, MulticastDNSPort); - mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v6, MulticastDNSPort); - if (!m->SuppressSending) m->SuppressSending = (m->timenow + mDNSPlatformOneSecond/10) | 1; // OR with one to ensure non-zero - 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->NewRData) - { - 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 - } - - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - CompleteDeregistration(m, rr); - else - { - rr->ImmedAnswer = mDNSNULL; - rr->v4Requester = zeroIPAddr; - rr->v6Requester = zerov6Addr; - } - } - verbosedebugf("SendResponses: Next in %d 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)->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 -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", - rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond); - } - - if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0) - m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)); - } - -#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 45) -#define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5) -#define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) - -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 - interval += mDNSRandom(interval/3); - rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; - rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond; - SetNextCacheCheckTime(m, rr); - } - debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, GetRRDisplayString(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->ThisQInterval <= InitialQuestionInterval*2; - 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; - CacheRecord *rr; - CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update - - for (rr=m->rrcache_hash[HashSlot(&q->qname)]; 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 >= 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 - { - *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=m->rrcache_hash[HashSlot(&q->qname)]; 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; - CacheRecord *rr; - domainname *target; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) - if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdnamehash == 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; iIPv4Available; // 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; iInterfaceID) - { - 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 = 0; - for (j=1; jtimenow + 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 = DomainNameLength(&q->qname) + 4; - CacheRecord *rr; - for (rr=m->rrcache_hash[HashSlot(&q->qname)]; 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) - { - 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) - { - mDNSu32 slot; - 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 - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) - 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->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 ones we're definitely going to send - for (q = m->Questions; q; q=q->next) - if (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 || (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, 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); // If 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->RecentAnswers = 0; - } - // 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; - CompleteProbing(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) - CompleteProbing(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; - DNSMessage query; - mDNSu8 *queryptr = query.data; - InitializeDNSMessage(&query.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 %lu forecast total %lu", - SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ", - q->qname.c, DNSTypeName(q->qtype), queryptr - query.data, queryptr + answerforecast - query.data); - // If we're suppressing this question, or we successfully put it, update its SendQNow state - if (SuppressOnThisInterface(q->DupSuppress, intf) || - BuildQuestion(m, &query, &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; - mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); - const mDNSu8 *const limit = query.data + ((query.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); - mDNSu8 *newptr = putQuestion(&query, 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)); - query.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(&query, queryptr, &query.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); - if (newptr) - { - verbosedebugf("SendQueries: Put %##s (%s) at %lu - %lu", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), queryptr - query.data, newptr - query.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 (query.h.numQuestions > 1) - LogMsg("SendQueries: Put %d answers; No more space for known answers", query.h.numAnswers); - query.h.flags.b[0] |= kDNSFlag0_TC; - break; - } - } - - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->IncludeInProbe) - { - mDNSu8 *newptr = PutResourceRecord(&query, queryptr, &query.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 > query.data) - { - if ((query.h.flags.b[0] & kDNSFlag0_TC) && query.h.numQuestions > 1) - LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet\n", query.h.numQuestions); - debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", - query.h.numQuestions, query.h.numQuestions == 1 ? "" : "s", - query.h.numAnswers, query.h.numAnswers == 1 ? "" : "s", - query.h.numAuthorities, query.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); - mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v4, MulticastDNSPort); - mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v6, MulticastDNSPort); - if (!m->SuppressSending) m->SuppressSending = (m->timenow + mDNSPlatformOneSecond/10) | 1; // OR with one to ensure non-zero - 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; - } - } - } - -// *************************************************************************** -#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)); - - rr->LastUsed = m->timenow; - rr->UseCount++; - 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); - } - - // CAUTION: MUST NOT do anything more with q after calling q->Callback(), 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. - 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 - } - -// 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 (ActiveQuestion(q) && ++q->RecentAnswers >= 10 && - q->ThisQInterval > InitialQuestionInterval*16 && 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++; - AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); - // 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 (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - 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); - } - AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse); - // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() - } - } - m->CurrentQuestion = mDNSNULL; - } - -mDNSlocal void ReleaseCacheRR(mDNS *const m, CacheRecord *r) - { - if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage) - mDNSPlatformMemFree(r->resrec.rdata); - r->resrec.rdata = mDNSNULL; - r->next = m->rrcache_free; - m->rrcache_free = r; - m->rrcache_totalused--; - } - -mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) - { - CacheRecord **rp = &(m->rrcache_hash[slot]); - - 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", GetRRDisplayString(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--; - } - m->rrcache_used[slot]--; - ReleaseCacheRR(m, rr); - } - else // else, not expired; see if we need to query - { - 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; - } - } - if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0) - m->NextCacheCheck = (event + CacheCheckGracePeriod(rr)); - rp = &rr->next; - } - } - if (m->rrcache_tail[slot] != rp) debugf("CheckCacheExpiration: Updating m->rrcache_tail[%d] from %p to %p", slot, m->rrcache_tail[slot], rp); - m->rrcache_tail[slot] = 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 - mDNSu32 slot = HashSlot(&q->qname); - - verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - - CheckCacheExpiration(m, slot); - 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 - for (rr=m->rrcache_hash[slot]; 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) 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; - } - -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->AnnounceCount = InitialAnnounceCount - 1; - 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 - } - -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("AnswerNewQuestion ERROR m->CurrentQuestion already set"); - m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted - - m->CurrentRecord = m->LocalOnlyRecords; - while (m->CurrentRecord && m->CurrentRecord != m->NewLocalOnlyRecords) - { - 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; - } - -mDNSlocal void AnswerLocalOnlyQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord) - { - if (m->CurrentQuestion) LogMsg("AnswerLocalOnlyQuestions 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)) - { - debugf("AnswerLocalOnlyQuestions %p %##s (%s) %lu", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); - AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); - // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() - } - } - m->CurrentQuestion = mDNSNULL; - } - -mDNSlocal void DiscardLocalOnlyRecords(mDNS *const m) - { - AuthRecord *rr = m->LocalOnlyRecords; - while (rr) - { - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - { AnswerLocalOnlyQuestions(m, rr, mDNSfalse); CompleteDeregistration(m, rr); return; } - if (rr->ProbeCount) { mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); return; } - rr=rr->next; - } - m->DiscardLocalOnlyRecords = mDNSfalse; - } - -mDNSlocal void AnswerForNewLocalOnlyRecords(mDNS *const m) - { - AuthRecord *rr = m->NewLocalOnlyRecords; - m->NewLocalOnlyRecords = m->NewLocalOnlyRecords->next; - AnswerLocalOnlyQuestions(m, rr, mDNStrue); - } - -mDNSlocal CacheRecord *GetFreeCacheRR(mDNS *const m, mDNSu16 RDLength) - { - CacheRecord *r = 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->MainCallback(m, mStatus_GrowCache); - } - - // 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++) - { - CacheRecord **rp = &(m->rrcache_hash[slot]); - while (*rp) - { - // Records that answer still-active questions are not candidates for deletion - if ((*rp)->CRActiveQuestion) - rp=&(*rp)->next; - else - { - CacheRecord *rr = *rp; - *rp = (*rp)->next; // Cut record from list - m->rrcache_used[slot]--; // Decrement counts - ReleaseCacheRR(m, rr); - } - } - if (m->rrcache_tail[slot] != rp) debugf("GetFreeCacheRR: Updating m->rrcache_tail[%d] from %p to %p", slot, m->rrcache_tail[slot], rp); - m->rrcache_tail[slot] = rp; - } - #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 - { - r = m->rrcache_free; - m->rrcache_free = r->next; - } - - if (r) - { - if (++m->rrcache_totalused >= m->rrcache_report) - { - debugf("RR Cache now using %ld records", m->rrcache_totalused); - if (m->rrcache_report < 100) m->rrcache_report += 10; - else m->rrcache_report += 100; - } - mDNSPlatformMemZero(r, sizeof(*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 { ReleaseCacheRR(m, r); r = mDNSNULL; } - } - } - - m->lock_rrcache = 0; - - return(r); - } - -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); - } - -mDNSlocal 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, mDNSPlatformTimeNow() + m->timenow_adjust); - m->timenow = mDNSPlatformTimeNow() + m->timenow_adjust; - 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 = mDNSPlatformTimeNow() + m->timenow_adjust; - if (m->timenow == 0) m->timenow = 1; - } - - if (m->timenow_last - m->timenow > 0) - { - m->timenow_adjust += m->timenow_last - m->timenow; - LogMsg("mDNSPlatformTimeNow 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) return(m->timenow); - if (m->NewLocalOnlyQuestions) return(m->timenow); - if (m->NewLocalOnlyRecords) return(m->timenow); - if (m->DiscardLocalOnlyRecords) return(m->timenow); - if (m->SuppressSending) return(m->SuppressSending); - 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); - } - -mDNSlocal 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); - } - -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++) CheckCacheExpiration(m, slot); - } - - // 4. See if we can answer any of our new local questions from the cache - for (i=0; m->NewQuestions && i<1000; i++) AnswerNewQuestion(m); - if (i >= 1000) debugf("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); - - for (i=0; m->DiscardLocalOnlyRecords && i<1000; i++) DiscardLocalOnlyRecords(m); - if (i >= 1000) debugf("mDNS_Execute: DiscardLocalOnlyRecords exceeded loop limit"); - - for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); - if (i >= 1000) debugf("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); - - for (i=0; m->NewLocalOnlyRecords && i<1000; i++) AnswerForNewLocalOnlyRecords(m); - if (i >= 1000) debugf("mDNS_Execute: AnswerLocalOnlyQuestions 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; - } - } - - m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary - } - - // Note about multi-threaded systems: - // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(), - // performing mDNS API operations that change our next scheduled event time. - // - // On multi-threaded systems (like the current Windows implementation) that have a single main thread - // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility - // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will - // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one - // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful - // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it - // does, the state of the signal will be noticed, causing the blocking primitive to return immediately - // without blocking. This avoids the race condition between the signal from the other thread arriving - // just *before* or just *after* the main thread enters the blocking primitive. - // - // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven, - // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to - // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer - // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale - // by the time it gets to the timer callback function). - - mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value - return(m->NextScheduledEvent); - } - -// 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; - LogMsg("mDNSResponder %s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow); - - if (sleepstate) - { - // 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->AnnounceCount < InitialAnnounceCount) - rr->ImmedAnswer = mDNSInterfaceMark; - SendResponses(m); - } - else - { - DNSQuestion *q; - mDNSu32 slot; - CacheRecord *cr; - - // 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->LastQTime = m->timenow - q->ThisQInterval; - q->RecentAnswers = 0; - ExpireDupSuppressInfo(q->DupSuppress, m->timenow); - m->NextScheduledQuery = m->timenow; - } - - // 2. Re-validate our cache records - m->NextCacheCheck = m->timenow; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (cr = m->rrcache_hash[slot]; cr; cr=cr->next) - mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForCableDisconnect); - - // 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); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; - 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 - -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)); - } - -#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; ih.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 - - debugf("CompareRData: How did we get here?"); - 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++) - { - LargeCacheRecord pkt; - ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, &pkt); - if (!ptr) break; - if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q)) - { - FoundUpdate = mDNStrue; - if (PacketRRConflict(m, our, &pkt.r)) - { - int result = (int)our->resrec.rrclass - (int)pkt.r.resrec.rrclass; - if (!result) result = (int)our->resrec.rrtype - (int)pkt.r.resrec.rrtype; - if (!result) result = CompareRData(our, &pkt.r); - switch (result) - { - case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); - break; - case 0: break; - case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); - mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); - return; - } - } - } - } - if (!FoundUpdate) - debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); - } - -mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr) - { - CacheRecord *rr; - for (rr = m->rrcache_hash[HashSlot(&pktrr->name)]; 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, - DNSMessage *const response) - { - 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; - mDNSBool delayresponse = mDNSfalse; - mDNSBool HaveUnicastAnswer = mDNSfalse; - const mDNSu8 *ptr = query->data; - mDNSu8 *responseptr = mDNSNULL; - AuthRecord *rr, *rr2; - int i; - - // If TC flag is set, it means we should expect that additional known answers may be coming in another packet. - if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNStrue; - - // *** - // *** 1. Parse Question Section and mark potential answers - // *** - for (i=0; ih.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)) - { - 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 unicast" - // (may also choose to do multicast as well) - // NR_AnswerTo == ~0 means "definitely answer via multicast" (can't downgrade to unicast later) - if (QuestionNeedsMulticastResponse) - { - // 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 should 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 = ptr; - } - } - } - - // 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) - { - CacheRecord *rr; - // 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 = mDNStrue; - - // 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 = m->rrcache_hash[HashSlot(&pktq.qname)]; 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 (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 - if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNStrue; - } - - // *** - // *** 3. Add additional records - // *** - 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(&nrp, rr->Additional1, rr); - - if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) - AddRecordToResponseList(&nrp, 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 (RRIsAddressType(rr2) && // For all address records (A/AAAA) ... - ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... - rr->resrec.rdnamehash == rr2->resrec.namehash && - SameDomainName(&rr->resrec.rdata->u.srv.target, &rr2->resrec.name)) // ... whose name is the name of the SRV target - AddRecordToResponseList(&nrp, rr2, rr); - } - - // *** - // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list - // *** - for (i=0; ih.numAnswers; i++) // For each record in the query's answer section... - { - // Get the record... - LargeCacheRecord pkt; - AuthRecord *rr; - CacheRecord *ourcacherr; - ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); - 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(&pkt.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(&pkt.r, rr)) - { - if (srcaddr->type == mDNSAddrType_IPv4) - { - if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zeroIPAddr; - } - 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; - } - } - - // 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, &pkt.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(&pkt.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(&pkt.r.resrec, q)) - { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } - else dqp = &q->NextInDQList; - } - } - } - - // *** - // *** 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; - - // 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 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) HaveUnicastAnswer = mDNStrue; - - if (SendMulticastResponse) - { - // 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; - m->NextScheduledResponse = m->timenow; - debugf("ProcessQuery: %##s (%s) : Will send on all interfaces", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - } - else - { - rr->ImmedAnswer = InterfaceID; // Record interface to send it on - m->NextScheduledResponse = m->timenow; - 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; - } - } - } - } - 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) - { - // Pick a random delay between 20ms and 120ms. - m->SuppressSending = m->timenow + (mDNSPlatformOneSecond*2 + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*10)) / 100; - if (m->SuppressSending == 0) m->SuppressSending = 1; - } - - // *** - // *** 8. If query is from a legacy client, generate a unicast response too - // *** - if (HaveUnicastAnswer) - responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); - -exit: - // *** - // *** 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, GetRRDisplayString(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, GetRRDisplayString(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, GetRRDisplayString(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) - { - DNSMessage response; - const mDNSu8 *responseend = mDNSNULL; - - verbosedebugf("Received Query from %#-15a:%d to %#-15a:%d on 0x%.8X with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", - srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1], - dstaddr, (mDNSu16)dstport.b[0]<<8 | dstport.b[1], - 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), &response); - - 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", - response.h.numQuestions, response.h.numQuestions == 1 ? "" : "s", - response.h.numAnswers, response.h.numAnswers == 1 ? "" : "s", - response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", - srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1], InterfaceID, srcaddr->type); - mDNSSendDNSMessage(m, &response, responseend, InterfaceID, dstport, srcaddr, srcport); - } - } - -// 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 mDNSAddr *dstaddr, - const mDNSInterfaceID InterfaceID, mDNSu8 ttl) - { - int i; - const mDNSu8 *ptr = LocateAnswers(response, end); // We ignore questions (if any) in a DNS response packet - CacheRecord *CacheFlushRecords = mDNSNULL; - 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 - - verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p TTL %d with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", - srcaddr, dstaddr, InterfaceID, ttl, - 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"); - - // TTL should be 255 - // In the case of overlayed subnets that aren't using RFC 3442, some packets may incorrectly - // go to the router first and then come back with a TTL of 254, so we allow that too. - // Anything lower than 254 is a pretty good sign of an off-net spoofing attack. - // Also, if we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us - if (ttl < 254 || (!mDNSAddrIsDNSMulticast(dstaddr) && (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)mDNSPlatformOneSecond)) - { - debugf("** Ignored apparent spoof mDNS Response from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", - srcaddr, dstaddr, ttl, 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"); - return; - } - - for (i = 0; i < totalrecords && ptr && ptr < end; i++) - { - LargeCacheRecord pkt; - const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd); - ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &pkt); - if (!ptr) break; // 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(&pkt.r, rr)) // If interface, name, type (if verified) and class match... - { - // ... check to see if rdata is identical - if (SameRData(&pkt.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 (pkt.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; - } - 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 - { - // else, the packet RR has different rdata -- check to see if this is a conflict - if (pkt.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &pkt.r)) - { - debugf("mDNSCoreReceiveResponse: Our Record: %08X %08X %s", rr-> resrec.rdatahash, rr-> resrec.rdnamehash, GetRRDisplayString(m, rr)); - debugf("mDNSCoreReceiveResponse: Pkt Record: %08X %08X %s", pkt.r.resrec.rdatahash, pkt.r.resrec.rdnamehash, GetRRDisplayString(m, &pkt.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 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 to re-assert our record(s) - // to rescue them (see note about "multi-homing and bridged networks" at the end of this function). - else if ((pkt.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 - { - mDNSu32 slot = HashSlot(&pkt.r.resrec.name); - CacheRecord *rr; - // 2a. Check if this packet resource record is already in our cache - for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) - { - // If we found this exact resource record, refresh its TTL - if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec)) - { - if (pkt.r.resrec.rdlength > InlineCacheRDSize) - verbosedebugf("Found record size %5d interface %p already in cache: %s", - pkt.r.resrec.rdlength, InterfaceID, GetRRDisplayString(m, &pkt.r)); - rr->TimeRcvd = m->timenow; - - if (pkt.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; } - - // 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 = pkt.r.resrec.RecordType; - } - } - - if (pkt.r.resrec.rroriginalttl > 0) - { - rr->resrec.rroriginalttl = pkt.r.resrec.rroriginalttl; - rr->UnansweredQueries = 0; - rr->MPUnansweredQ = 0; - rr->MPUnansweredKA = 0; - rr->MPExpectingKA = mDNSfalse; - } - 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 && pkt.r.resrec.rroriginalttl > 0) - { - rr = GetFreeCacheRR(m, pkt.r.resrec.rdlength); - if (!rr) debugf("No cache space to add record for %#s", pkt.r.resrec.name.c); - else - { - RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer - *rr = pkt.r; - rr->resrec.rdata = saveptr; // and then restore it after the structure assignment - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) - { *cfp = rr; cfp = &rr->NextInCFList; } - // If this is an oversized record with external storage allocated, copy rdata to external storage - if (pkt.r.resrec.rdlength > InlineCacheRDSize) - mDNSPlatformMemCopy(pkt.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + pkt.r.resrec.rdlength); - rr->next = mDNSNULL; // Clear 'next' pointer - *(m->rrcache_tail[slot]) = rr; // Append this record to tail of cache slot list - m->rrcache_tail[slot] = &(rr->next); // Advance tail pointer - m->rrcache_used[slot]++; - //debugf("Adding RR %##s to cache (%d)", pkt.r.name.c, m->rrcache_used); - CacheRecordAdd(m, rr); - // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us - SetNextCacheCheckTime(m, rr); - } - } - } - } - - // 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 *r1 = CacheFlushRecords, *r2; - CacheFlushRecords = CacheFlushRecords->NextInCFList; - r1->NextInCFList = mDNSNULL; - for (r2 = m->rrcache_hash[HashSlot(&r1->resrec.name)]; r2; r2=r2->next) - if (SameResourceRecordSignature(&r1->resrec, &r2->resrec) && m->timenow - r2->TimeRcvd > mDNSPlatformOneSecond) - { - verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name.c, DNSTypeName(r2->resrec.rrtype)); - // 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); - } - } - } - -mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID, mDNSu8 ttl) - { - const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; - const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; - const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); - - // Read the integer parts which are in IETF byte-order (MSB first, LSB second) - 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]); - - 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); - if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); - else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, dstaddr, InterfaceID, ttl); - else debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]); - - // 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 - -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, - 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->LastQTime = question->LastQTime; - q->RecentAnswers = 0; - q->DuplicateOf = FindDuplicateQuestion(m, q); - q->LastQTxTime = question->LastQTxTime; - SetNextQueryTime(m,q); - } - } - -mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) - { -#if TEST_LOCALONLY_FOR_EVERYTHING - question->InterfaceID = (mDNSInterfaceID)~0; -#endif - 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 == ((mDNSInterfaceID)~0)) 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, make sure it exists - if (question->InterfaceID && question->InterfaceID != ((mDNSInterfaceID)~0)) - { - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->InterfaceID == question->InterfaceID) break; - if (!intf) - { - debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID); - return(mStatus_BadReferenceErr); - } - } - - // 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 (!ValidateDomainName(&question->qname)) - { - LogMsg("Attempt to start query with invalid qname %##s %s", question->qname.c, DNSTypeName(question->qtype)); - return(mStatus_Invalid); - } - - if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); - - question->next = mDNSNULL; - question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() - question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question - question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization - question->RecentAnswers = 0; - question->CurrentAnswers = 0; - question->LargeAnswers = 0; - question->UniqueAnswers = 0; - question->DuplicateOf = FindDuplicateQuestion(m, question); - question->NextInDQList = mDNSNULL; - for (i=0; iDupSuppress[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 (%p) started", - question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question); - else - verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p (%p) duplicate of (%p)", - question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question, question->DuplicateOf); - - *q = question; - if (question->InterfaceID == ((mDNSInterfaceID)~0)) - { - 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) - { - CacheRecord *rr; - DNSQuestion **q = &m->Questions; - if (question->InterfaceID == ((mDNSInterfaceID)~0)) 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 = m->rrcache_hash[HashSlot(&question->qname)]; 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 %X", 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, mDNSQuestionCallback *Callback, void *Context) - { - question->ThisQInterval = -1; // Indicate that query is not yet active - question->InterfaceID = InterfaceID; - question->qtype = kDNSType_PTR; - question->qclass = kDNSClass_IN; - question->QuestionCallback = Callback; - question->QuestionContext = Context; - if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); - return(mDNS_StartQuery(m, question)); - } - -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_internal(m, &query->qAv4); - mDNS_StartQuery_internal(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_internal(m, &query->qAv4); - mDNS_StopQuery_internal(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_internal(m, &query->qAv4); - mDNS_StartQuery_internal(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, - ((mDNSu16)answer->rdata->u.srv.port.b[0] << 8) | answer->rdata->u.srv.port.b[1]); - 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; - 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; - if (!AddRecord) return; - - if (answer->rrtype == kDNSType_A) - { - query->info->ip.type = mDNSAddrType_IPv4; - query->info->ip.ip.v4 = answer->rdata->u.ip; - } - 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%d: %##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) - { - if (answer->rrtype == kDNSType_A) - debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ip); - else - debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6); - } - 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; // This question not yet in the question list - query->qSRV.InterfaceID = info->InterfaceID; - AssignDomainName(query->qSRV.qname, info->name); - query->qSRV.qtype = kDNSType_SRV; - query->qSRV.qclass = kDNSClass_IN; - query->qSRV.QuestionCallback = FoundServiceInfoSRV; - query->qSRV.QuestionContext = query; - - query->qTXT.ThisQInterval = -1; // This question not yet in the question list - query->qTXT.InterfaceID = info->InterfaceID; - AssignDomainName(query->qTXT.qname, info->name); - query->qTXT.qtype = kDNSType_TXT; - query->qTXT.qclass = kDNSClass_IN; - query->qTXT.QuestionCallback = FoundServiceInfoTXT; - query->qTXT.QuestionContext = query; - - query->qAv4.ThisQInterval = -1; // This question not yet in the question list - query->qAv4.InterfaceID = info->InterfaceID; - query->qAv4.qname.c[0] = 0; - query->qAv4.qtype = kDNSType_A; - query->qAv4.qclass = kDNSClass_IN; - query->qAv4.QuestionCallback = FoundServiceInfo; - query->qAv4.QuestionContext = query; - - query->qAv6.ThisQInterval = -1; // This question not yet in the question list - query->qAv6.InterfaceID = info->InterfaceID; - query->qAv6.qname.c[0] = 0; - query->qAv6.qtype = kDNSType_AAAA; - query->qAv6.qclass = kDNSClass_IN; - 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; - - 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 *query) - { - mDNS_Lock(m); - if (query->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qSRV); - if (query->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qTXT); - if (query->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qAv4); - if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qAv6); - mDNS_Unlock(m); - } - -mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, - const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) - { - MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType]); - question->InterfaceID = InterfaceID; - question->qtype = kDNSType_PTR; - question->qclass = kDNSClass_IN; - question->QuestionCallback = Callback; - question->QuestionContext = Context; - return(mDNS_StartQuery(m, question)); - } - -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - Responder Functions -#endif - -// Set up a AuthRecord with sensible default values. -// These defaults may be overwritten with new values before mDNS_Register is called -mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, - mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) - { - // 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 = kDefaultTTLforShared; - - // Field Group 1: Persistent metadata for Authoritative Records - rr->Additional1 = mDNSNULL; - rr->Additional2 = mDNSNULL; - rr->DependentOn = mDNSNULL; - rr->RRSet = mDNSNULL; - rr->RecordCallback = Callback; - rr->RecordContext = Context; - - rr->resrec.RecordType = RecordType; - rr->HostTarget = mDNSfalse; - - // Field Group 2: Transient state for Authoritative Records (set in mDNS_Register_internal) - // Field Group 3: Transient state for Cache Records (set in mDNS_Register_internal) - - // Field Group 4: The actual information pertaining to this resource record - rr->resrec.InterfaceID = InterfaceID; - rr->resrec.name.c[0] = 0; // MUST be set by client - 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); - } - } - -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) - { - mDNS_Lock(m); - - if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) - { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(m, &rr->resrec, &newrdata->u)); return(mStatus_Invalid); } - - // If TTL is unspecified, leave TTL unchanged - if (newttl == 0) newttl = rr->resrec.rroriginalttl; - - // If we already have an update queued up which has not gone through yet, - // give the client a chance to free that memory - if (rr->NewRData) - { - RData *n = rr->NewRData; - rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... - if (rr->UpdateCallback) - rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary - } - - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); - InitializeLastAPTime(m, rr); - rr->NewRData = newrdata; - rr->newrdlength = newrdlength; - rr->UpdateCallback = Callback; - if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; - if (!rr->NextUpdateCredit) rr->NextUpdateCredit = (m->timenow + mDNSPlatformOneSecond * 60) | 1; - if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); - if (rr->UpdateCredits <= 5) - { - mDNSs32 delay = 1 << (5 - rr->UpdateCredits); - if (!rr->UpdateBlocked) rr->UpdateBlocked = (m->timenow + delay * mDNSPlatformOneSecond) | 1; - rr->LastAPTime = rr->UpdateBlocked; - rr->ThisAPInterval *= 4; - LogMsg("Excessive update rate for %##s; delaying announcement by %d seconds", rr->resrec.name.c, delay); - } - 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); - } - -mDNSlocal void 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 mDNS_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 - - mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kDefaultTTLforUnique, kDNSRecordTypeUnique, HostNameCallback, set); - mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kDefaultTTLforUnique, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); - mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kDefaultTTLforUnique, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); - - // 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->hostname); - if (set->ip.type == mDNSAddrType_IPv4) - { - set->RR_A.resrec.rrtype = kDNSType_A; - set->RR_A.resrec.rdata->u.ip = 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_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->hostname); - 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 mDNS_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 mDNS_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_GenerateFQDN(mDNS *const m) - { - domainname newname; - mDNS_Lock(m); - - newname.c[0] = 0; - if (!AppendDomainLabel(&newname, &m->hostlabel)) LogMsg("ERROR! Cannot create dot-local hostname"); - if (!AppendLiteralLabelString(&newname, "local")) LogMsg("ERROR! Cannot create dot-local hostname"); - if (!SameDomainName(&m->hostname, &newname)) - { - NetworkInterfaceInfo *intf; - AuthRecord *rr; - - m->hostname = newname; - - // 1. Stop advertising our address records on all interfaces - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) mDNS_DeadvertiseInterface(m, intf); - - // 2. Start advertising our address records using the new name - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) mDNS_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); - } - -mDNSlocal void 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("HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), msg, result); - } - #endif - - if (result == mStatus_NoError) - { - // Notify the client that the host name is successfully registered - if (m->MainCallback) - m->MainCallback(m, result); - } - else if (result == mStatus_NameConflict) - { - domainlabel oldlabel = m->hostlabel; - - // 1. First give the client callback a chance to pick a new name - if (m->MainCallback) - m->MainCallback(m, mStatus_NameConflict); - - // 2. If the client callback didn't do it, add (or increment) an index ourselves - 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_GenerateFQDN(m); - } - } - -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->TxAndRx) active->IPv4Available = mDNStrue; - if (intf->ip.type == mDNSAddrType_IPv6 && intf->TxAndRx) active->IPv6Available = mDNStrue; - } - } - -mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set) - { - mDNSBool FirstOfType = mDNStrue; - NetworkInterfaceInfo **p = &m->HostInterfaces; - mDNS_Lock(m); - - // Assume this interface will be active - set->InterfaceActive = mDNStrue; - set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->TxAndRx); - set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->TxAndRx); - - while (*p) - { - if (*p == set) - { - LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list"); - mDNS_Unlock(m); - return(mStatus_AlreadyRegistered); - } - - // This InterfaceID is already in the list, so mark this interface inactive for now - if ((*p)->InterfaceID == set->InterfaceID) - { - set->InterfaceActive = mDNSfalse; - if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse; - if (set->ip.type == mDNSAddrType_IPv4 && set->TxAndRx) (*p)->IPv4Available = mDNStrue; - if (set->ip.type == mDNSAddrType_IPv6 && set->TxAndRx) (*p)->IPv6Available = mDNStrue; - } - - p=&(*p)->next; - } - - set->next = mDNSNULL; - *p = set; - - debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set->InterfaceID, &set->ip, - set->InterfaceActive ? - "not represented in list; marking active and retriggering queries" : - "already represented in list; marking inactive for now"); - - // 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. - // 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 ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive) - { - DNSQuestion *q; - AuthRecord *rr; - // 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 to all go and hit the network with identical queries at exactly the same moment. - if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); - 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 - q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - q->LastQTime = m->timenow - q->ThisQInterval; - q->RecentAnswers = 0; - if (ActiveQuestion(q)) m->NextScheduledQuery = m->timenow; - } - - // 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 < ReannounceCount) - rr->AnnounceCount = ReannounceCount; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); - InitializeLastAPTime(m, rr); - } - } - - if (set->Advertise) - mDNS_AdvertiseInterface(m, set); - - 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) - { - 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) - { - debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active", - set->InterfaceID); - 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 - { - CacheRecord *rr; - DNSQuestion *q; - mDNSu32 slot; - debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant", - set->InterfaceID); - - // 1. Deactivate any questions specific to this interface - for (q = m->Questions; q; q=q->next) - if (q->InterfaceID == set->InterfaceID) - q->ThisQInterval = 0; - - // 2. Flush any cache records received on this interface - revalidate = mDNSfalse; // Don't revalidate if we're flushing the records - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) - if (rr->resrec.InterfaceID == set->InterfaceID) - PurgeCacheResourceRecord(m, rr); - } - } - - // If we were advertising on this interface, deregister those address and reverse-lookup records now - if (set->Advertise) - mDNS_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; - CacheRecord *rr; - m->NextCacheCheck = m->timenow; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) - if (rr->resrec.InterfaceID == set->InterfaceID) - mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForCableDisconnect); - } - - 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 - - // 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 - sr->RR_PTR.AnnounceCount = InitialAnnounceCount; // Make sure we don't send a goodbye for the PTR record - 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 record 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; iNumSubTypes; 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); - } - -// 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. "_printer._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->hostname1) 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; - - // Initialize the AuthRecord objects to sane values - mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeAdvisory, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kDefaultTTLforUnique, kDNSRecordTypeUnique, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kDefaultTTLforUnique, 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*)"\x05_mdns\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; iSubTypes[i].resrec.name; - mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); - if (ConstructServiceName(&sr->SubTypes[i].resrec.name, &s, type, 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; - - // 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; - - 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; iSubTypes[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) - { - mStatus result = mStatus_UnknownErr; - ExtraResourceRecord **e = &sr->Extras; - while (*e) e = &(*e)->next; - - // If TTL is unspecified, make it the same as the service's TXT and SRV default - if (ttl == 0) ttl = kDefaultTTLforUnique; - - 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); - extra->r.DependentOn = &sr->RR_SRV; - - debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name.c); - - result = mDNS_Register(m, &extra->r); - if (!result) *e = extra; - return result; - } - -mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra) - { - ExtraResourceRecord **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); - return(mStatus_BadReferenceErr); - } - - debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name.c); - - *e = (*e)->next; - return(mDNS_Deregister(m, &extra->r)); - } - -mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname) - { - domainlabel name; - domainname type, domain; - domainname *host = mDNSNULL; - ExtraResourceRecord *extras = sr->Extras; - mStatus err; - - DeconstructServiceName(&sr->RR_SRV.resrec.name, &name, &type, &domain); - if (!newname) - { - IncrementLabelSuffix(&name, mDNStrue); - newname = &name; - } - debugf("Reregistering as %#s", 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 (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) - { - debugf("Service set for %##s already deregistered", sr->RR_PTR.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_PTR.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; iNumSubTypes; 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, kDefaultTTLforUnique, 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, kDefaultTTLforShared, 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 - -mDNSexport void mDNS_GrowCache(mDNS *const m, CacheRecord *storage, mDNSu32 numrecords) - { - if (storage && numrecords) - { - mDNSu32 i; - for (i=0; irrcache_free; - m->rrcache_free = storage; - m->rrcache_size += numrecords; - } - } - -mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, - CacheRecord *rrcachestorage, mDNSu32 rrcachesize, - mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) - { - mDNSu32 slot; - mDNSs32 timenow; - mStatus result = mDNSPlatformTimeInit(&timenow); - if (result != mStatus_NoError) return(result); - - if (!rrcachestorage) rrcachesize = 0; - - m->p = p; - m->KnownBugs = 0; - m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; - m->mDNSPlatformStatus = mStatus_Waiting; - m->MainCallback = Callback; - m->MainContext = Context; - - // 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 - m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section - m->timenow_last = timenow; - m->timenow_adjust = 0; - 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->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; - m->rrcache_tail[slot] = &m->rrcache_hash[slot]; - m->rrcache_used[slot] = 0; - } - - mDNS_GrowCache(m, rrcachestorage, rrcachesize); - - // Fields below only required for mDNS Responder... - m->hostlabel.c[0] = 0; - m->nicelabel.c[0] = 0; - m->hostname.c[0] = 0; - m->HIHardware.c[0] = 0; - m->HISoftware.c[0] = 0; - m->ResourceRecords = mDNSNULL; - m->DuplicateRecords = mDNSNULL; - m->LocalOnlyRecords = mDNSNULL; - m->NewLocalOnlyRecords = mDNSNULL; - m->DiscardLocalOnlyRecords = mDNSfalse; - m->CurrentRecord = mDNSNULL; - m->HostInterfaces = mDNSNULL; - m->ProbeFailTime = 0; - m->NumFailedProbes = 0; - m->SuppressProbes = 0; - - result = mDNSPlatformInit(m); - - return(result); - } - -mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) - { - m->mDNSPlatformStatus = result; - if (m->MainCallback) - m->MainCallback(m, mStatus_NoError); - } - -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; - - rrcache_totalused = m->rrcache_totalused; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - { - while (m->rrcache_hash[slot]) - { - CacheRecord *rr = m->rrcache_hash[slot]; - m->rrcache_hash[slot] = rr->next; - if (rr->CRActiveQuestion) rrcache_active++; - m->rrcache_used[slot]--; - ReleaseCacheRR(m, rr); - } - // Reset tail pointer back to empty state (not that it really matters on exit, but we'll do it anyway, for the sake of completeness) - m->rrcache_tail[slot] = &m->rrcache_hash[slot]; - } - debugf("mDNS_Close: RR Cache was using %ld records, %d 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); - - m->Questions = mDNSNULL; // We won't be answering any more questions! - - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) - mDNS_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; - m->CurrentRecord = rr->next; - 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); - } - } - - 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 - while (m->ResourceRecords) - SendResponses(m); - - mDNS_Unlock(m); - debugf("mDNS_Close: mDNSPlatformClose"); - mDNSPlatformClose(m); - debugf("mDNS_Close: done"); - } diff --git a/src/mdns/mDNSClientAPI.h b/src/mdns/mDNSClientAPI.h deleted file mode 100644 index 2bfa76bc..00000000 --- a/src/mdns/mDNSClientAPI.h +++ /dev/null @@ -1,1423 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log$ -Revision 1.2 2004/03/02 00:03:37 rpedde -Merge new rendezvous code - -Revision 1.114.2.3 2003/12/05 00:03:34 cheshire - Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256 - -Revision 1.114.2.2 2003/12/04 23:30:00 cheshire -Add "#define MAX_ESCAPED_DOMAIN_NAME 1005", needed for Posix folder to build - -Revision 1.114.2.1 2003/12/03 11:07:58 cheshire -: Stop and start of a service uses old ip address (with old port number) - -Revision 1.114 2003/08/29 19:44:15 cheshire - 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 - Traffic reduction: No need to announce record for longer than TTL - -Revision 1.111 2003/08/21 02:21:50 cheshire - Efficiency: Reduce repeated queries - -Revision 1.110 2003/08/20 23:39:31 cheshire - 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 - 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 - 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 - 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 - 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 - UpdateRecord not working right -Added "newrdlength" field to hold new length of updated rdata - -Revision 1.102 2003/08/16 03:39:00 cheshire - InterfaceID -1 indicates "local only" - -Revision 1.101 2003/08/15 20:16:02 cheshire - 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 - Include cache records in SIGINFO output -Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them - -Revision 1.99 2003/08/14 02:17:05 cheshire - 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 - 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 - Improve cache performance -Changed the number of hash table slots from 37 to 499 - -Revision 1.95 2003/08/09 00:55:02 cheshire - 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 - Guard against time going backwards - -Revision 1.92 2003/08/08 18:36:04 cheshire - 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: Feature: New Rendezvous APIs (#7875) (mDNSResponder component) -Added error type for incompatibility between daemon and client versions - -Revision 1.85 2003/07/19 03:23:13 cheshire - 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 - Remove mDNSResponder version from packet header and use HINFO record instead - -Revision 1.82 2003/07/17 17:35:04 cheshire - 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 - Need to implement "unicast response" request, using top bit of qclass - -Revision 1.80 2003/07/15 01:55:12 cheshire - Need to implement service registration with subtypes - -Revision 1.79 2003/07/13 02:28:00 cheshire - 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 - No more local.arpa - -Revision 1.75 2003/07/02 21:19:45 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.74 2003/07/02 02:41:23 cheshire - mDNSResponder needs to start with a smaller cache and then grow it as needed - -Revision 1.73 2003/06/10 04:24:39 cheshire - 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 - No need for multiple machines to all be sending the same queries - -Revision 1.70 2003/06/07 04:50:53 cheshire - 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 - When query produces zero results, call mDNS_Reconfirm() on any antecedent records - -Revision 1.67 2003/06/07 01:22:14 cheshire - mDNSResponder needs an mDNS_Reconfirm() function - -Revision 1.66 2003/06/07 00:59:43 cheshire - 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 - 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 - Duplicate registrations not handled as efficiently as they should be - -Revision 1.60 2003/05/31 00:09:49 cheshire - Add ability to discover what services are on a network - -Revision 1.59 2003/05/29 06:11:35 cheshire -: 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 - 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 - 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 - 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 - 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 - 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 - Change timenow from a local variable to a structure member - -Revision 1.43 2003/04/25 01:45:56 cheshire - mDNS_RegisterNoSuchService needs to include a host name - -Revision 1.42 2003/04/15 20:58:31 jgraessl - -Bug #: 3229014 -Added a hash to lookup records in the cache. - -Revision 1.41 2003/04/15 18:09:13 jgraessl - -Bug #: 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 - 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 - 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 - 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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 -Bug #: 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: -3086540 computer name changes not handled properly -3124348 service name changes are not properly handled -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-existance -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 - -*/ - -#ifndef __mDNSClientAPI_h -#define __mDNSClientAPI_h - -#include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration -#include "mDNSDebug.h" - -#ifdef __cplusplus - extern "C" { -#endif - -// *************************************************************************** -// Function scope indicators - -// If you see "mDNSlocal" before a function name, it means the function is not callable outside this file -#ifndef mDNSlocal -#define mDNSlocal static -#endif -// If you see "mDNSexport" before a symbol, it means the symbol is exported for use by clients -#ifndef mDNSexport -#define mDNSexport -#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 - - 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; -typedef signed long mDNSs32; -typedef unsigned long mDNSu32; - -// 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 { 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 union { mDNSu8 b[2]; mDNSu16 NotAnInteger; } mDNSOpaque16; -typedef union { mDNSu8 b[4]; mDNSu32 NotAnInteger; } mDNSOpaque32; -typedef union { 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) - -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, // 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, - // = -65550, - mStatus_Incompatible = -65551, - mStatus_BadInterfaceErr = -65552, - - // -65553 - -65789 currently unused - - // Non-error values: - mStatus_GrowCache = -65790, - mStatus_ConfigChanged = -65791, - mStatus_MemFree = -65792 // 0xFFFE FF00 - }; - -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 - -// *************************************************************************** -#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. ten seconds) -// -- 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 - - kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - - kDNSRecordTypePacketAdd = 0x80, // Received in the Additional Section of a DNS Response - kDNSRecordTypePacketAddUnique = 0xA0, // Received in the Additional Section of a DNS Response with kDNSClass_UniqueRRSet set - kDNSRecordTypePacketAns = 0xC0, // Received in the Answer Section of a DNS Response - kDNSRecordTypePacketAnsUnique = 0xE0, // Received in the Answer Section of a DNS Response with kDNSClass_UniqueRRSet set - - kDNSRecordTypePacketAnsMask = 0x40, // True for PacketAns and PacketAnsUnique - kDNSRecordTypePacketUniqueMask = 0x20 // True for PacketAddUnique and PacketAnsUnique - }; - -typedef struct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; -typedef struct { mDNSu16 preference; domainname exchange; } rdataMX; - -// 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 64 -// 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 -#define InlineCacheRDSize 64 - -typedef union - { - mDNSu8 data[StandardAuthRDSize]; - mDNSv4Addr ip; // For 'A' record - mDNSv6Addr ipv6; // For 'AAAA' record - domainname name; // For PTR and CNAME records - UTF8str255 txt; // For TXT record - rdataSRV srv; // For SRV record - rdataMX mx; // For MX 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 ResourceRecord_struct ResourceRecord; -typedef struct DNSQuestion_struct DNSQuestion; -typedef struct mDNS_struct mDNS; -typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; - -// 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); - -struct ResourceRecord_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; // 32-bit hash of the raw rdata - mDNSu32 rdnamehash; // Set if this rdata contains a domain name (e.g. PTR, SRV, CNAME etc.) - RData *rdata; // Pointer to storage for this rdata - }; - -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 - ResourceRecord resrec; - - // 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 - 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 - - // 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 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) - 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 - - 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 - }; - -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 NextRequiredQuery; // In platform time units - mDNSs32 LastUsed; // In platform time units - mDNSu32 UseCount; // Number of times this RR has been used to answer a question - 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 - }; - -typedef struct - { - CacheRecord r; - mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes - } LargeCacheRecord; - -typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; - -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; // InterfaceActive is set if interface is sending & receiving packets - // InterfaceActive is clear if interface is here to represent an address with A - // and/or AAAA records, but there is already an earlier representative for this - // physical interface which will be used for the actual sending & receiving - // packets (this status may change as interfaces are added and removed) - 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; - mDNSAddr ip; - mDNSBool Advertise; // Set Advertise to false if you are only searching on this interface - mDNSBool TxAndRx; // Set to false if not sending and receiving packets on this interface - }; - -typedef struct ExtraResourceRecord_struct ExtraResourceRecord; -struct ExtraResourceRecord_struct - { - ExtraResourceRecord *next; - 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. - 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._mdns._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; - -// 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 LastQTime; // Last scheduled tranmission of this Q on *all* applicable interfaces - mDNSs32 ThisQInterval; // LastQTime + ThisQInterval is the next scheduled tranmission 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 - mDNSu32 RecentAnswers; // 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 - 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 - mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces - - // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() - mDNSInterfaceID InterfaceID; // Non-zero if you want to issue link-local queries only on a single specific IP interface - domainname qname; - mDNSu16 qtype; - mDNSu16 qclass; - 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 - 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 - }; - -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 AdvertiseLocalAddresses; - mStatus mDNSPlatformStatus; - 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; - char MsgBuffer[80]; // Temp storage used while building error log messages - - // Task Scheduling variables - mDNSs32 timenow; // The time that this particular activation of the mDNS code started - mDNSs32 timenow_last; // The time the last time we ran - mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards - 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 - 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 ~0 ("local only") - 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; - CacheRecord *rrcache_free; - CacheRecord *rrcache_hash[CACHE_HASH_SLOTS]; - CacheRecord **rrcache_tail[CACHE_HASH_SLOTS]; - mDNSu32 rrcache_used[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 hostname; // 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 *LocalOnlyRecords; // Local records registered with InterfaceID set to ~0 ("local only") - AuthRecord *NewLocalOnlyRecords; // Fresh local-only records not yet delivered to local-only questions - mDNSBool DiscardLocalOnlyRecords;// Set when we have "remove" events we need to deliver to local-only questions - AuthRecord *CurrentRecord; // Next AuthRecord about to be examined - NetworkInterfaceInfo *HostInterfaces; - mDNSs32 ProbeFailTime; - mDNSs32 NumFailedProbes; - mDNSs32 SuppressProbes; - }; - -// *************************************************************************** -#if 0 -#pragma mark - Useful Static Constants -#endif - -extern const mDNSIPPort zeroIPPort; -extern const mDNSv4Addr zeroIPAddr; -extern const mDNSv6Addr zerov6Addr; -extern const mDNSv4Addr onesIPv4Addr; -extern const mDNSv6Addr onesIPv6Addr; -extern const mDNSInterfaceID mDNSInterface_Any; - -extern const mDNSIPPort UnicastDNSPort; -extern const mDNSIPPort MulticastDNSPort; -extern const mDNSv4Addr AllDNSAdminGroup; -extern const mDNSv4Addr AllDNSLinkGroup; -extern const mDNSv6Addr AllDNSLinkGroupv6; -extern const mDNSAddr AllDNSLinkGroup_v4; -extern const mDNSAddr AllDNSLinkGroup_v6; - -// *************************************************************************** -#if 0 -#pragma mark - Main Client Functions -#endif - -// Every client should call mDNS_Init, passing in storage for the mDNS object, mDNS_PlatformSupport object, and rrcache. -// The rrcachesize parameter is the size of (i.e. number of entries in) the rrcache array passed in. -// Most clients 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). -// -// 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, - CacheRecord *rrcachestorage, mDNSu32 rrcachesize, - mDNSBool AdvertiseLocalAddresses, - mDNSCallback *Callback, void *Context); -#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, CacheRecord *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); - -// *************************************************************************** -#if 0 -#pragma mark - Platform support functions that are accessible to the client layer too -#endif - -extern mDNSs32 mDNSPlatformOneSecond; -extern mDNSs32 mDNSPlatformTimeNow(void); - -// *************************************************************************** -#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_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); -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, 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_DomainTypeRegistration = 2, - mDNS_DomainTypeRegistrationDefault = 3 - } mDNS_DomainType; - -extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, 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); - -// 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 -#endif - -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 char *DNSTypeName(mDNSu16 rrtype); -extern char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd); -#define GetRRDisplayString(m, rr) GetRRDisplayString_rdb((m), &(rr)->resrec, &(rr)->resrec.rdata->u) -extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); -extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); - -// *************************************************************************** -#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() - -typedef struct - { - 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 struct - { - DNSMessageHeader h; // Note: Size 12 bytes - mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 - } DNSMessage; - -// 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. -// 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). -extern mStatus mDNSPlatformInit (mDNS *const m); -extern void mDNSPlatformClose (mDNS *const m); -extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, mDNSIPPort srcport, 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 mStatus mDNSPlatformTimeInit (mDNSs32 *timenow); - -// The core mDNS code provides these functions, for the platform support code to call at appropriate times -// -// mDNS_GenerateFQDN() is called once on startup (typically from mDNSPlatformInit()) -// and then again on each subsequent change of the dot-local 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) -// -// 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_GenerateFQDN(mDNS *const m); -extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set); -extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set); -extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); -extern void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, - const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl); -extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); - -// *************************************************************************** -#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]; - }; - -// *************************************************************************** - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/src/mdns/mDNSDebug.h b/src/mdns/mDNSDebug.h deleted file mode 100644 index ed63537c..00000000 --- a/src/mdns/mDNSDebug.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log$ -Revision 1.2 2004/03/02 00:03:37 rpedde -Merge new rendezvous code - -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 - Update copyright notices, etc., in source code comments - -Revision 1.12 2003/05/26 03:01:27 cheshire - 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 - -*/ - -#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 - -//#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 void LogMsg(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/src/mdns/mDNSPlatformFunctions.h b/src/mdns/mDNSPlatformFunctions.h deleted file mode 100644 index 3bb60742..00000000 --- a/src/mdns/mDNSPlatformFunctions.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log$ -Revision 1.2 2004/03/02 00:03:37 rpedde -Merge new rendezvous code - -Revision 1.22.2.1 2003/12/05 00:03:34 cheshire - Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256 - -Revision 1.22 2003/08/18 22:53:37 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() - -Revision 1.21 2003/08/15 20:16:57 cheshire -Update comment for mDNSResponder takes too much RPRVT - -Revision 1.20 2003/08/12 19:56:24 cheshire -Update to APSL 2.0 - -Revision 1.19 2003/08/05 22:20:15 cheshire - Need to check IP TTL on responses - -Revision 1.18 2003/07/22 23:57:20 cheshire -Move platform-layer function prototypes from mDNSClientAPI.h to mDNSPlatformFunctions.h where they belong - -Revision 1.17 2003/07/19 03:15:15 cheshire -Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, -and add the obvious trivial implementations to each platform support layer - -Revision 1.16 2003/07/02 21:19:46 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.15 2003/05/23 22:39:45 cheshire - Need to adjust maximum packet size for IPv6 - -Revision 1.14 2003/04/28 21:54:57 cheshire -Fix compiler warning - -Revision 1.13 2003/03/15 04:40:36 cheshire -Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" - -Revision 1.12 2003/02/21 01:54:08 cheshire -Bug #: 3099194 mDNSResponder needs performance improvements -Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") - -Revision 1.11 2002/12/23 22:13:29 jgraessl - -Reviewed by: Stuart Cheshire -Initial IPv6 support for mDNSResponder. - -Revision 1.10 2002/09/21 20:44:49 zarzycki -Added APSL info - -Revision 1.9 2002/09/19 04:20:43 cheshire -Remove high-ascii characters that confuse some systems - -Revision 1.8 2002/09/16 23:12:14 cheshire -Minor code tidying - -Revision 1.7 2002/09/16 18:41:42 cheshire -Merge in license terms from Quinn's copy, in preparation for Darwin release - -*/ - -// Note: All moved to mDNSClientAPI.h diff --git a/src/mdns/mDNSPosix.c b/src/mdns/mDNSPosix.c deleted file mode 100755 index 42d68e22..00000000 --- a/src/mdns/mDNSPosix.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - * - * 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 , - * 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$ -Revision 1.4 2004/03/02 00:03:37 rpedde -Merge new rendezvous code - -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 - mDNSResponder divide by zero in mDNSPlatformTimeNow() - -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 - 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 - 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 - Rationalize naming of domainname manipulation functions - -Revision 1.10 2003/04/25 01:45:57 cheshire - 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 -*/ - -#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above -#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below -#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mDNSUNP.h" - -// *************************************************************************** -// Structures - -// 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 multicastSocket; - int multicastSocketv6; - }; - -// *************************************************************************** -// 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; - -// 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 -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); - if (gMDNSPlatformPosixVerboseLevel >= 1) - fprintf(stderr, "%s\n", buffer); - fflush(stderr); - } - -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); - if (gMDNSPlatformPosixVerboseLevel >= 2) - fprintf(stderr, "%s\n", buffer); - fflush(stderr); - } - -mDNSexport void LogMsg(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); - } - -#define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr) - -static 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; - } - -#ifdef mDNSIPv6Support - case AF_INET6: - { - struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; - assert(sin6->sin6_len == sizeof(*sin6)); - 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 DNSMessage *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort) - { - int err; - struct sockaddr_storage to; - PosixNetworkInterface * thisIntf; - - assert(m != NULL); - assert(msg != NULL); - assert(end != NULL); - assert( (((char *) end) - ((char *) msg)) > 0 ); - assert(InterfaceID != 0); // Can't send from zero source address - assert(srcPort.NotAnInteger != 0); // Nor from a zero source port - assert(dstPort.NotAnInteger != 0); // Nor from a zero source port - - 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; - } - -#ifdef mDNSIPv6Support - else if (dst->type == mDNSAddrType_IPv6) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; - mDNSPlatformMemZero(sin6, sizeof(*sin6)); - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = dstPort.NotAnInteger; - sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; - } -#endif - - err = 0; - thisIntf = (PosixNetworkInterface *)(InterfaceID); - if (dst->type == mDNSAddrType_IPv4) - err = sendto(thisIntf->multicastSocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); - -#ifdef mDNSIPv6Support - else if (dst->type == mDNSAddrType_IPv6) - err = sendto(thisIntf->multicastSocketv6, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); -#endif - - if (err > 0) err = 0; - else if (err < 0) - verbosedebugf("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); - - return PosixErrorToStatus(err); - } - -// This routine is called when the main loop detects that data is available on a socket. -static 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; - mDNSBool reject; - - assert(m != NULL); - assert(intf != NULL); - assert(skt >= 0); - - fromLen = sizeof(from); - flags = 0; - packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo); - - if (packetLen >= 0) - { - SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); - SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); - - // If we have broken IP_RECVDSTADDR functionality (so far - // I've only seen this on OpenBSD) then apply a hack to - // convince mDNS Core that this isn't a spoof packet. - // Basically what we do is check to see whether the - // packet arrived as a multicast and, if so, set its - // destAddr to the mDNS address. - // - // I must admit that I could just be doing something - // wrong on OpenBSD and hence triggering this problem - // but I'm at a loss as to how. - // - // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have - // no way to tell the destination address or interface this packet arrived on, - // so all we can do is just assume it's a multicast - - #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) - if ( (destAddr.NotAnInteger == 0) && (flags & MSG_MCAST) ) - { - destAddr.type == senderAddr.type; - if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup; - 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 ( 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", - &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, - &intf->coreIntf.ip, intf->intfName, intf->index); - 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", - &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index); - num_pkts_accepted++; - } - } - - if (packetLen >= 0 && packetLen < (ssize_t)sizeof(DNSMessageHeader)) - { - debugf("SocketDataReady packet length (%d) too short", packetLen); - packetLen = -1; - } - - if (packetLen >= 0) - mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, - &senderAddr, senderPort, &destAddr, MulticastDNSPort, intf->coreIntf.InterfaceID, 255); - } - -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark ***** Init and Term -#endif - -// 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) - { - MakeDomainLabelFromLiteralString(namelabel, "Fill in Default Service Name Here"); - } - -// 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; - } - -// Searches the interface list looking for the named interface. -// Returns a pointer to if it found, or NULL otherwise. -static 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; - } - -// Frees the specified PosixNetworkInterface structure. The underlying -// interface must have already been deregistered with the mDNS core. -static void FreePosixNetworkInterface(PosixNetworkInterface *intf) - { - assert(intf != NULL); - if (intf->intfName != NULL) free((void *)intf->intfName); - if (intf->multicastSocket != -1) assert(close(intf->multicastSocket) == 0); - if (intf->multicastSocketv6 != -1) assert(close(intf->multicastSocketv6) == 0); - free(intf); - } - -// Grab the first interface, deregister it, free it, and repeat until done. -static void ClearInterfaceList(mDNS *const m) - { - assert(m != NULL); - - while (m->HostInterfaces) - { - PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces); - mDNS_DeregisterInterface(m, &intf->coreIntf); - 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 multicast send/receive socket for the specified -// port on the interface specified by the IP addrelss intfAddr. -static 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; - - (void) interfaceIndex; // Unused - 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); -#ifdef mDNSIPv6Support - 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 (err == 0) - { - #if defined(SO_REUSEPORT) - err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); - #elif defined(SO_REUSEADDR) - err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); - #else - #error This platform has no way to avoid address busy errors on multicast. - #endif - if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } - } - - // We want to receive destination addresses and interface identifiers. - if (intfAddr->sa_family == AF_INET) - { - struct ip_mreq imr; - struct sockaddr_in bindAddr; - if (err == 0) - { - #if defined(IP_PKTINFO) // Linux - err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } - #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris - #if defined(IP_RECVDSTADDR) - err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } - #endif - #if defined(IP_RECVIF) - if (err == 0) - { - err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); - if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } - } - #endif - #else - #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts - #endif - } - - // Add multicast group membership on this interface - if (err == 0) - { - imr.imr_multiaddr.s_addr = AllDNSLinkGroup.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) - { - 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) - -#ifdef mDNSIPv6Support - else if (intfAddr->sa_family == AF_INET6) - { - struct ipv6_mreq imr6; - struct sockaddr_in6 bindAddr6; - if (err == 0) - { - #if defined(IPV6_PKTINFO) - 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 - } - - // Add multicast group membership on this interface - if (err == 0) - { - imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroupv6; - imr6.ipv6mr_interface = interfaceIndex; - 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) - { - 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)); - bindAddr6.sin6_len = sizeof(bindAddr6); - bindAddr6.sin6_family = AF_INET6; - bindAddr6.sin6_port = port.NotAnInteger; - bindAddr6.sin6_flowinfo = 0; -// bindAddr6.sin6_addr.s_addr = IN6ADDR_ANY_INIT; // 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. -static int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, const char *intfName) - { - int err = 0; - PosixNetworkInterface *intf; - PosixNetworkInterface *alias = NULL; - - assert(m != NULL); - assert(intfAddr != NULL); - assert(intfName != NULL); - - // Allocate the interface structure itself. - intf = 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); - intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; - - // Set up the extra fields in PosixNetworkInterface. - assert(intf->intfName != NULL); // intf->intfName already set up above - intf->index = if_nametoindex(intf->intfName); - intf->multicastSocket = -1; - intf->multicastSocketv6 = -1; - 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->multicastSocket == -1 && intfAddr->sa_family == AF_INET) - err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket); -#ifdef mDNSIPv6Support - else if (alias->multicastSocketv6 == -1 && intfAddr->sa_family == AF_INET6) - err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocketv6); -#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); - - // 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; - } - -static 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; - -#ifdef mDNSIPv6Support - 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) -#ifdef mDNSIPv6Support - || (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_name) == 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_name); - } - - // Clean up. - if (intfList != NULL) free_ifi_info(intfList); - return err; - } - -// mDNS core calls this routine to initialise the platform-specific data. -mDNSexport mStatus mDNSPlatformInit(mDNS *const m) - { - int err; - assert(m != NULL); - - // 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, "Macintosh"); - - // Set up the RFC 1034-compliant label - m->hostlabel.c[0] = 0; - GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); - if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh"); - - mDNS_GenerateFQDN(m); - - // Tell mDNS core about the network interfaces on this machine. - err = SetupInterfaceList(m); - - // 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 == 0) - 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); - } - -extern 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 mDNSs32 mDNSPlatformOneSecond = 1024; - -mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) - { - // 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 - *timenow = mDNSPlatformTimeNow(); - return(mStatus_NoError); - } - -mDNSexport mDNSs32 mDNSPlatformTimeNow() - { - 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 void mDNSPosixGetFDSet(mDNS *const 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); - while (info) - { - if (info->multicastSocket != -1) - { - if (*nfds < info->multicastSocket + 1) - *nfds = info->multicastSocket + 1; - FD_SET(info->multicastSocket, readfds); - } - if (info->multicastSocketv6 != -1) - { - if (*nfds < info->multicastSocketv6 + 1) - *nfds = info->multicastSocketv6 + 1; - FD_SET(info->multicastSocketv6, readfds); - } - info = (PosixNetworkInterface *)(info->coreIntf.next); - } - - // 3. Calculate the time remaining to the next scheduled event (in struct timeval format) - ticks = nextevent - mDNSPlatformTimeNow(); - 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); - while (info) - { - if (info->multicastSocket != -1 && FD_ISSET(info->multicastSocket, readfds)) - { - FD_CLR(info->multicastSocket, readfds); - SocketDataReady(m, info, info->multicastSocket); - } - if (info->multicastSocketv6 != -1 && FD_ISSET(info->multicastSocketv6, readfds)) - { - FD_CLR(info->multicastSocketv6, readfds); - SocketDataReady(m, info, info->multicastSocketv6); - } - info = (PosixNetworkInterface *)(info->coreIntf.next); - } - } diff --git a/src/mdns/mDNSPosix.h b/src/mdns/mDNSPosix.h deleted file mode 100755 index b66f5a0e..00000000 --- a/src/mdns/mDNSPosix.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log$ -Revision 1.2 2004/03/02 00:03:37 rpedde -Merge new rendezvous code - -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 - 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 - -*/ - -#ifndef __mDNSPlatformPosix_h -#define __mDNSPlatformPosix_h - -#include - -#if HAVE_IPV6 -#define mDNSIPv6Support 1 -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -// This is a global because debugf_() needs to be able to check its value -extern int gMDNSPlatformPosixVerboseLevel; - -struct mDNS_PlatformSupport_struct - { - // No additional data required for Posix at this time - long dummy[1]; // Some compilers don't like empty structures - }; - -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 *const m, int *nfds, fd_set *readfds, struct timeval *timeout); -extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds); - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/src/mdns/mDNSUNP.c b/src/mdns/mDNSUNP.c deleted file mode 100755 index 8896d303..00000000 --- a/src/mdns/mDNSUNP.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log$ -Revision 1.3 2004/03/02 00:03:37 rpedde -Merge new rendezvous code - -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 - 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 - -*/ - -#include "mDNSUNP.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -/* Solaris defined SIOCGIFCONF etc in but - other platforms don't even have that include file. So, - if we haven't yet got a definition, let's try to find - . -*/ - -#ifndef SIOCGIFCONF - #include -#endif - -/* sockaddr_dl is only referenced if we're using IP_RECVIF, - so only include the header in that case. -*/ - -#ifdef IP_RECVIF - #include -#endif - - -struct ifi_info *get_ifi_info(int family, int doaliases) -{ - int junk; - struct ifi_info *ifi, *ifihead, **ifipnext; - int sockfd, len, lastlen, flags, myflags; - char *ptr, *buf, lastname[IFNAMSIZ], *cptr; - struct ifconf ifc; - struct ifreq *ifr, ifrcopy; - struct sockaddr_in *sinptr; - -#if defined(AF_INET6) && defined(HAVE_IPV6) - struct sockaddr_in6 *sinptr6; -#endif - - sockfd = -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 = 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; - - len = GET_SA_LEN(ifr->ifr_addr); - ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ - -// fprintf(stderr, "intf %d 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 = 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 */ - ifi->ifi_index = if_nametoindex(ifr->ifr_name); - 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 = calloc(1, sizeof(struct sockaddr_in)); - if (ifi->ifi_addr == NULL) { - goto gotError; - } - memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); - -#ifdef SIOCGIFBRDADDR - if (flags & IFF_BROADCAST) { - if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) { - goto gotError; - } - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; - ifi->ifi_brdaddr = 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; - ifi->ifi_dstaddr = 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) && defined(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.__u6_addr.__u6_addr16[1] = 0; - memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6)); - } - 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); - } - 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); - 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) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t n; - -#ifdef CMSG_FIRSTHDR - struct cmsghdr *cmptr; - union { - struct cmsghdr cm; - char control[1024]; - } control_un; - - msg.msg_control = control_un.control; - msg.msg_controllen = sizeof(control_un.control); - msg.msg_flags = 0; -#else - memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */ -#endif /* CMSG_FIRSTHDR */ - - msg.msg_name = (void *) sa; - msg.msg_namelen = *salenptr; - iov[0].iov_base = 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); - int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1); - pktp->ipi_ifindex = sdl->sdl_index; -#ifndef HAVE_BROKEN_RECVIF_NAME - strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen); -#endif - assert(pktp->ipi_ifname[IFI_NAME - 1] == 0); - // null terminated because of memset above - continue; - } -#endif - -#if defined(IPV6_PKTINFO) && defined(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; - sin6->sin6_len = sizeof(*sin6); - 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 - assert(0); // unknown ancillary data - } - return(n); -#endif /* CMSG_FIRSTHDR */ -} diff --git a/src/mdns/mDNSUNP.h b/src/mdns/mDNSUNP.h deleted file mode 100755 index bbea8773..00000000 --- a/src/mdns/mDNSUNP.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log$ -Revision 1.3 2004/03/16 05:11:18 rpedde -Fix obvious typecast bug - -Revision 1.2 2004/03/02 00:03:37 rpedde -Merge new rendezvous code - -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 - 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 - -*/ - -#ifndef __mDNSUNP_h -#define __mDNSUNP_h - -#include -#include -#include -#include - -#ifdef __cplusplus - extern "C" { -#endif - -#ifdef NOT_HAVE_SOCKLEN_T - typedef unsigned int socklen_t; -#endif - -#if !defined(_SS_MAXSIZE) - #define sockaddr_storage sockaddr -#endif - -#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 mDNSIPv6Support -#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 */ -#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 */ -}; - -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); - -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 */ - short ifi_myflags; /* our own IFI_xxx flags */ - int ifi_index; /* interface index */ - struct sockaddr *ifi_addr; /* primary address */ - struct sockaddr *ifi_brdaddr;/* broadcast address */ - struct sockaddr *ifi_dstaddr;/* destination address */ - struct ifi_info *ifi_next; /* next of these structures */ -}; - -#define IFI_ALIAS 1 /* ifi_addr is an alias */ - -extern struct ifi_info *get_ifi_info(int family, int doaliases); -extern void free_ifi_info(struct ifi_info *); - -#ifdef __cplusplus - } -#endif - -#endif