diff options
| author | pettai <pettai@NetBSD.org> | 2014-03-24 00:31:13 +0000 |
|---|---|---|
| committer | pettai <pettai@NetBSD.org> | 2014-03-24 00:31:13 +0000 |
| commit | ee6991e1b7cc8d076911533626a48497011b4ebe (patch) | |
| tree | ab13b4baf49c2ca4837848adf0298165992cd523 /external/apache2 | |
| parent | 09fa7e98e105d1936a7dab2aa5f48698d0f027ff (diff) | |
Import mDNSResponder-258.14, merge, fix conflicts
Diffstat (limited to 'external/apache2')
14 files changed, 5195 insertions, 6735 deletions
diff --git a/external/apache2/mDNSResponder/dist/Clients/dns-sd.c b/external/apache2/mDNSResponder/dist/Clients/dns-sd.c index 8ce9089560d..9dc5388bb29 100644 --- a/external/apache2/mDNSResponder/dist/Clients/dns-sd.c +++ b/external/apache2/mDNSResponder/dist/Clients/dns-sd.c @@ -70,6 +70,14 @@ cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Rele // aren't in the system's /usr/lib/libSystem.dylib. //#define TEST_NEW_CLIENTSTUB 1 +// When building mDNSResponder for Mac OS X 10.4 and earlier, /usr/lib/libSystem.dylib is built using its own private +// copy of dnssd_clientstub.c, which is old and doesn't have all the entry points defined in the latest version, so +// when we're building dns-sd.c on Mac OS X 10.4 or earlier, we automatically set TEST_NEW_CLIENTSTUB so that we'll +// embed a copy of the latest dnssd_clientstub.c instead of trying to link to the incomplete version in libSystem.dylib +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1040 +#define TEST_NEW_CLIENTSTUB 1 +#endif + #include <ctype.h> #include <stdio.h> // For stdout, stderr #include <stdlib.h> // For exit() @@ -144,6 +152,15 @@ cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Rele return name; } + static size_t _sa_len(const struct sockaddr *addr) + { + if (addr->sa_family == AF_INET) return (sizeof(struct sockaddr_in)); + else if (addr->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6)); + else return (sizeof(struct sockaddr)); + } + +# define SA_LEN(addr) (_sa_len(addr)) + #else #include <unistd.h> // For getopt() and optind #include <netdb.h> // For getaddrinfo() @@ -153,14 +170,18 @@ cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Rele #include <arpa/inet.h> // For inet_addr() #include <net/if.h> // For if_nametoindex() static const char kFilePathSep = '/'; + #define SA_LEN(addr) ((addr)->sa_len) #endif #if (TEST_NEW_CLIENTSTUB && !defined(__APPLE_API_PRIVATE)) #define __APPLE_API_PRIVATE 1 #endif +// DNSServiceSetDispatchQueue is not supported on 10.6 & prior +#if ! TEST_NEW_CLIENTSTUB && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ % 10) <= 1060) +#undef _DNS_SD_LIBDISPATCH +#endif #include "dns_sd.h" - #include "ClientCommon.h" #if TEST_NEW_CLIENTSTUB @@ -196,12 +217,24 @@ static char myhinfoX[ 9] = "\003Mac\004OS X"; static char updatetest[3] = "\002AA"; static char bigNULL[8192]; // 8K is maximum rdata we support +#if _DNS_SD_LIBDISPATCH +dispatch_queue_t main_queue; +dispatch_source_t timer_source; +#endif + // Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this #define LONG_TIME 100000000 static volatile int stopNow = 0; static volatile int timeOut = LONG_TIME; +#if _DNS_SD_LIBDISPATCH +#define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) \ + if (main_queue && (E) == kDNSServiceErr_ServiceNotRunning) { fprintf(stderr, "Error code %d\n", (E)); exit(0); } +#else +#define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) +#endif + //************************************************************************************************************* // Supporting Utility Functions @@ -311,6 +344,7 @@ static void DNSSD_API enum_reply(DNSServiceRef sdref, const DNSServiceFlags flag (void)sdref; // Unused (void)ifIndex; // Unused (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); // 1. Print the header if (num_printed++ == 0) printf("Timestamp Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing"); @@ -443,6 +477,7 @@ static void DNSSD_API zonedata_browse(DNSServiceRef sdref, const DNSServiceFlags (void)sdref; // Unused (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); if (!(flags & kDNSServiceFlagsAdd)) return; if (errorCode) { printf("Error code %d\n", errorCode); return; } @@ -458,6 +493,8 @@ static void DNSSD_API browse_reply(DNSServiceRef sdref, const DNSServiceFlags fl char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; (void)sdref; // Unused (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-25s %s\n", "Domain", "Service Type", "Instance Name"); printtimestamp(); if (errorCode) printf("Error code %d\n", errorCode); @@ -512,11 +549,13 @@ static void DNSSD_API resolve_reply(DNSServiceRef sdref, const DNSServiceFlags f (void)sdref; // Unused (void)ifIndex; // Unused (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - printtimestamp(); - if (errorCode) printf("Error code %d\n", errorCode); + if (errorCode) + printf("Error code %d\n", errorCode); else { + printtimestamp(); printf("%s can be reached at %s:%u (interface %d)", fullname, hosttarget, PortAsNumber, ifIndex); if (flags) printf(" Flags: %X", flags); // Don't show degenerate TXT records containing nothing but a single empty string @@ -571,6 +610,11 @@ static void myTimerCallBack(void) err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0); if (err) printf("Failed: %d\n", err); else printf("Succeeded\n"); timeOut = LONG_TIME; +#if _DNS_SD_LIBDISPATCH + if (timer_source) + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); +#endif } break; } @@ -588,6 +632,7 @@ static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags (void)sdref; // Unused (void)flags; // Unused (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); printtimestamp(); printf("Got a reply for service %s.%s%s: ", name, regtype, domain); @@ -596,7 +641,15 @@ static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags { if (flags & kDNSServiceFlagsAdd) printf("Name now registered and active\n"); else printf("Name registration removed\n"); - if (operation == 'A' || operation == 'U' || operation == 'N') timeOut = 5; + if (operation == 'A' || operation == 'U' || operation == 'N') + { + timeOut = 5; +#if _DNS_SD_LIBDISPATCH + if (timer_source) + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); +#endif + } } else if (errorCode == kDNSServiceErr_NameConflict) { @@ -633,6 +686,7 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, (void)ifIndex; // Unused (void)ttl; // Unused (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); printtimestamp(); @@ -697,9 +751,10 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, uint32_t publicAddress, uint32_t protocol, uint16_t privatePort, uint16_t publicPort, uint32_t ttl, void *context) { (void)sdref; // Unused - (void)context; // Unused (void)flags; // Unused - + (void)context; // Unused + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + if (num_printed++ == 0) printf("Timestamp if %-20s %-15s %-15s %-15s %-6s\n", "External Address", "Protocol", "Internal Port", "External Port", "TTL"); printtimestamp(); if (errorCode && errorCode != kDNSServiceErr_DoubleNAT) printf("Error code %d\n", errorCode); @@ -711,7 +766,8 @@ static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceF snprintf(addr, sizeof(addr), "%d.%d.%d.%d", digits[0], digits[1], digits[2], digits[3]); printf("%-4d %-20s %-15d %-15d %-15d %-6d%s\n", ifIndex, addr, protocol, ntohs(privatePort), ntohs(publicPort), ttl, errorCode == kDNSServiceErr_DoubleNAT ? " Double NAT" : ""); } - fflush(stdout); + + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } #endif @@ -722,7 +778,8 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, char addr[256] = ""; (void) sdref; (void) context; - + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-44s %s\n", "Hostname", "Address", "TTL"); printtimestamp(); @@ -759,6 +816,26 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, // The main test function static void HandleEvents(void) +#if _DNS_SD_LIBDISPATCH + { + main_queue = dispatch_get_main_queue(); + if (client) DNSServiceSetDispatchQueue(client, main_queue); + if (client_pa) DNSServiceSetDispatchQueue(client_pa, main_queue); + if (operation == 'A' || operation == 'U' || operation == 'N') + { + timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_queue); + if (timer_source) + { + // Start the timer "timeout" seconds into the future and repeat it every "timeout" seconds + dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), + (uint64_t)timeOut * NSEC_PER_SEC, 0); + dispatch_source_set_event_handler(timer_source, ^{myTimerCallBack();}); + dispatch_resume(timer_source); + } + } + dispatch_main(); + } +#else { int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1; int dns_sd_fd2 = client_pa ? DNSServiceRefSockFD(client_pa) : -1; @@ -801,6 +878,7 @@ static void HandleEvents(void) } } } +#endif static int getfirstoption(int argc, char **argv, const char *optstr, int *pOptInd) // Return the recognized option in optstr and the option index of the next arg. @@ -832,9 +910,10 @@ static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordR char *name = (char *)context; (void)service; // Unused - (void)rec; // Unused + (void)rec; // Unused (void)flags; // Unused - + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); + printtimestamp(); printf("Got a reply for record %s: ", name); @@ -846,28 +925,26 @@ static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordR } if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); // DNSServiceRemoveRecord(service, rec, 0); to test record removal - } -static unsigned long getip(const char *const name) - { - unsigned long ip = 0; - struct addrinfo hints; - struct addrinfo *addrs = NULL; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - - if (getaddrinfo(name, NULL, &hints, &addrs) == 0) +#if 0 // To test updating of individual records registered via DNSServiceRegisterRecord + if (!errorCode) { - ip = ((struct sockaddr_in*) addrs->ai_addr)->sin_addr.s_addr; + int x = 0x11111111; + printf("Updating\n"); + DNSServiceUpdateRecord(service, rec, 0, sizeof(x), &x, 0); } +#endif - if (addrs) - { - freeaddrinfo(addrs); - } + if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + } - return(ip); +static void getip(const char *const name, struct sockaddr_storage *result) + { + struct addrinfo *addrs = NULL; + int err = getaddrinfo(name, NULL, NULL, &addrs); + if (err) fprintf(stderr, "getaddrinfo error %d for %s", err, name); + else memcpy(result, addrs->ai_addr, SA_LEN(addrs->ai_addr)); + if (addrs) freeaddrinfo(addrs); } static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip) @@ -876,10 +953,15 @@ static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const // On the Win32 platform, WinSock must be initialized for getip() to succeed. // Any DNSService* call will initialize WinSock for us, so we make sure // DNSServiceCreateConnection() is called before getip() is. - unsigned long addr = getip(ip); - return(DNSServiceRegisterRecord(sdref, &record, kDNSServiceFlagsUnique, opinterface, host, - kDNSServiceType_A, kDNSServiceClass_IN, sizeof(addr), &addr, 240, MyRegisterRecordCallback, (void*)host)); - // Note, should probably add support for creating proxy AAAA records too, one day + struct sockaddr_storage hostaddr; + getip(ip, &hostaddr); + if (hostaddr.ss_family == AF_INET) + return(DNSServiceRegisterRecord(sdref, &record, kDNSServiceFlagsUnique, opinterface, host, + kDNSServiceType_A, kDNSServiceClass_IN, 4, &((struct sockaddr_in *)&hostaddr)->sin_addr, 240, MyRegisterRecordCallback, (void*)host)); + else if (hostaddr.ss_family == AF_INET6) + return(DNSServiceRegisterRecord(sdref, &record, kDNSServiceFlagsUnique, opinterface, host, + kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host)); + else return(kDNSServiceErr_BadParam); } #define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ @@ -973,6 +1055,14 @@ int main(int argc, char **argv) printf("Using LocalOnly\n"); } + if (argc > 1 && (!strcmp(argv[1], "-p2p") || !strcmp(argv[1], "-P2P"))) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexP2P; + printf("Using P2P\n"); + } + if (argc > 2 && !strcmp(argv[1], "-i")) { opinterface = if_nametoindex(argv[2]); @@ -983,7 +1073,7 @@ int main(int argc, char **argv) } if (argc < 2) goto Fail; // Minimum command line is the command name and one argument - operation = getfirstoption(argc, argv, "EFBZLRPQCAUNTMISV" + operation = getfirstoption(argc, argv, "EFBZLRPQqCAUNTMISV" #if HAS_NAT_PMP_API "X" #endif @@ -1055,10 +1145,12 @@ int main(int argc, char **argv) //DNSServiceRemoveRecord(client_pa, record, 0); break; + case 'q': case 'Q': case 'C': { uint16_t rrtype, rrclass; DNSServiceFlags flags = kDNSServiceFlagsReturnIntermediates; + if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable; if (argc < opi+1) goto Fail; rrtype = (argc <= opi+1) ? kDNSServiceType_A : GetRRType(argv[opi+1]); rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]); @@ -1137,7 +1229,7 @@ int main(int argc, char **argv) #endif case 'S': { - Opaque16 registerPort = { { 0x23, 0x45 } }; + Opaque16 registerPort = { { 0x23, 0x45 } }; // 9029 decimal unsigned char txtrec[16] = "\xF" "/path=test.html"; DNSRecordRef rec; unsigned char nulrec[4] = "1234"; diff --git a/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.c b/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.c index 4fab9dd2052..f08d9c3d07b 100644 --- a/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.c +++ b/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.c @@ -13,552 +13,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - - Change History (most recent first): - -Log: DNSCommon.c,v $ -Revision 1.252 2009/06/27 00:27:03 cheshire -<rdar://problem/6959273> mDNSResponder taking up 13% CPU with 400 KBps incoming bonjour requests -Removed overly-complicate and ineffective multi-packet known-answer snooping code -(Bracketed it with "#if ENABLE_MULTI_PACKET_QUERY_SNOOPING" for now; will delete actual code later) - -Revision 1.251 2009/05/19 23:40:37 cheshire -<rdar://problem/6903507> Sleep Proxy: Retransmission logic not working reliably on quiet networks -Added m->NextScheduledSPRetry timer for scheduling Sleep Proxy registration retries - -Revision 1.250 2009/05/01 21:28:33 cheshire -<rdar://problem/6721680> AppleConnectAgent's reachability checks delay sleep by 30 seconds -No longer suspend network operations after we've acknowledged that the machine is going to sleep, -because other software may not have yet acknowledged the sleep event, and may be still trying -to do unicast DNS queries or other Bonjour operations. - -Revision 1.249 2009/04/24 00:29:20 cheshire -<rdar://problem/3476350> Return negative answers when host knows authoritatively that no answer exists -Added support for generating/parsing/displaying NSEC records - -Revision 1.248 2009/04/23 22:11:16 cheshire -Minor cleanup in debugging checks in GetLargeResourceRecord - -Revision 1.247 2009/04/21 23:36:25 cheshire -<rdar://problem/6814427> Remove unused kDNSType_MAC - -Revision 1.246 2009/04/21 01:00:19 cheshire -Fixed typo in previous checkin - -Revision 1.245 2009/04/21 00:57:23 cheshire -<rdar://problem/6810410> Off-by-one error in putDomainNameAsLabels() -If just writing one-byte root label, make sure we have space for that - -Revision 1.244 2009/04/11 00:19:30 jessic2 -<rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically - -Revision 1.243 2009/04/01 17:50:10 mcguire -cleanup mDNSRandom - -Revision 1.242 2009/03/26 04:01:55 jessic2 -<rdar://problem/6613786> MessageTracer: Log service types longer than 14 characters and service types with underscores - -Revision 1.241 2009/03/18 20:50:08 cheshire -<rdar://problem/6650064> uDNS: Reverse lookup of own IP address takes way too long, sometimes forever - -Revision 1.240 2009/03/18 20:41:04 cheshire -Added definition of the all-ones mDNSOpaque16 ID - -Revision 1.239 2009/03/06 23:51:50 mcguire -Fix broken build by defining DiscardPort - -Revision 1.238 2009/03/04 00:40:13 cheshire -Updated DNS server error codes to be more consistent with definitions at -<http://www.iana.org/assignments/dns-parameters> - -Revision 1.237 2009/03/03 23:04:43 cheshire -For clarity, renamed "MAC" field to "HMAC" (Host MAC, as opposed to Interface MAC) - -Revision 1.236 2009/03/03 22:51:53 cheshire -<rdar://problem/6504236> Sleep Proxy: Waking on same network but different interface will cause conflicts - -Revision 1.235 2009/02/07 05:55:44 cheshire -Only pay attention to m->DelaySleep when it's nonzero - -Revision 1.234 2009/02/07 02:52:52 cheshire -<rdar://problem/6084043> Sleep Proxy: Need to adopt IOPMConnection -Pay attention to m->DelaySleep when computing next task time - -Revision 1.233 2009/01/30 23:50:31 cheshire -Added LastLabel() routine to get the last label of a domainname - -Revision 1.232 2009/01/15 00:22:48 mcguire -<rdar://problem/6437092> NAT-PMP: mDNSResponder needs to listen on 224.0.0.1:5350/UDP with REUSEPORT - -Revision 1.231 2008/12/12 01:24:06 cheshire -Updated GetNextScheduledEvent() to pay attention to m->SPSProxyListChanged - -Revision 1.230 2008/12/10 01:55:54 cheshire -Renamed "Max" macro to avoid conflict with another "Max" macro on ARMv5 - -Revision 1.229 2008/11/27 01:28:45 cheshire -For display purposes, show sleep sequence number as unsigned - -Revision 1.228 2008/11/26 20:57:37 cheshire -For consistency with other similar macros, renamed mdnsIsDigit/mdnsIsLetter/mdnsValidHostChar -to mDNSIsDigit/mDNSIsLetter/mDNSValidHostChar - -Revision 1.227 2008/11/26 20:28:05 cheshire -Added new SSHPort constant - -Revision 1.226 2008/11/16 16:55:51 cheshire -Updated debugging messages - -Revision 1.225 2008/11/14 21:56:31 cheshire -Moved debugging routine ShowTaskSchedulingError() from daemon.c into DNSCommon.c - -Revision 1.224 2008/11/14 02:20:03 cheshire -Include m->NextScheduledSPS in task scheduling calculations - -Revision 1.223 2008/11/14 01:19:03 cheshire -Initialize TimeRcvd and TimeExpire fields in AuthRecord_struct - -Revision 1.222 2008/11/14 00:00:53 cheshire -After client machine wakes up, Sleep Proxy machine need to remove any records -it was temporarily holding as proxy for that client - -Revision 1.221 2008/11/13 19:06:02 cheshire -Added code to put, get, and display rdataOPT properly - -Revision 1.220 2008/11/06 01:08:11 mcguire -Fix compiler warning about discarding const - -Revision 1.219 2008/11/04 23:06:50 cheshire -Split RDataBody union definition into RDataBody and RDataBody2, and removed -SOA from the normal RDataBody union definition, saving 270 bytes per AuthRecord - -Revision 1.218 2008/11/04 22:21:44 cheshire -Changed zone field of AuthRecord_struct from domainname to pointer, saving 252 bytes per AuthRecord - -Revision 1.217 2008/11/04 22:13:43 cheshire -Made RDataBody parameter to GetRRDisplayString_rdb "const" - -Revision 1.216 2008/11/04 20:06:19 cheshire -<rdar://problem/6186231> Change MAX_DOMAIN_NAME to 256 - -Revision 1.215 2008/10/23 23:54:35 cheshire -Added missing "const" in declaration - -Revision 1.214 2008/10/23 22:25:55 cheshire -Renamed field "id" to more descriptive "updateid" - -Revision 1.213 2008/10/22 01:01:52 cheshire -Added onesEthAddr constant, used for sending ARP broadcasts - -Revision 1.212 2008/10/14 21:52:18 cheshire -Added support for putting/getting/printing kDNSType_MAC - -Revision 1.211 2008/10/09 22:36:08 cheshire -Now that we have Sleep Proxy Server, can't suppress normal scheduling logic while going to sleep - -Revision 1.210 2008/10/08 01:03:52 cheshire -Change GetFirstActiveInterface() so the NetworkInterfaceInfo it returns is not "const" -Added mDNS_SetupQuestion() convenience function - -Revision 1.209 2008/09/23 04:13:30 cheshire -<rdar://problem/6238774> Remove "local" from the end of _services._dns-sd._udp PTR records -Removed old special-case Bonjour Browser hack that is no longer needed - -Revision 1.208 2008/09/23 02:33:56 cheshire -<rdar://problem/4738033> uDNS: Should not compress SRV rdata in uDNS packets - -Revision 1.207 2008/09/23 02:30:07 cheshire -Get rid of PutResourceRecordCappedTTL() - -Revision 1.206 2008/09/23 02:26:09 cheshire -Don't need to export putEmptyResourceRecord (it's only used from DNSCommon.c) - -Revision 1.205 2008/09/23 02:21:00 cheshire -Don't need to force setting of rrclass in PutResourceRecordTTLWithLimit() now that putLLQ() sets it correctly - -Revision 1.204 2008/08/29 19:03:05 cheshire -<rdar://problem/6185645> Off-by-one error in putDomainNameAsLabels() - -Revision 1.203 2008/08/13 00:47:53 mcguire -Handle failures when packet logging - -Revision 1.202 2008/08/13 00:32:48 mcguire -refactor to use SwapDNSHeaderBytes instead of swapping manually - -Revision 1.201 2008/07/24 20:23:03 cheshire -<rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning - -Revision 1.200 2008/07/18 00:07:50 cheshire -<rdar://problem/5904999> Log a message for applications that register service types longer than 14 characters - -Revision 1.199 2008/03/14 19:58:38 mcguire -<rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client -Make sure we add the record when sending LLQ refreshes - -Revision 1.198 2008/03/07 23:29:24 cheshire -Fixed cosmetic byte order display issue in DumpPacket output - -Revision 1.197 2008/03/05 22:51:29 mcguire -<rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client -Even further refinements - -Revision 1.196 2008/03/05 22:01:53 cheshire -<rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client -Now that we optionally add the HINFO record, when rewriting the header fields into network byte -order, we need to use our updated msg->h.numAdditionals, not the stack variable numAdditionals - -Revision 1.195 2008/03/05 19:06:30 mcguire -<rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client -further refinements - -Revision 1.194 2008/03/05 00:26:06 cheshire -<rdar://problem/5500969> BTMM: Need ability to identify version of mDNSResponder client - -Revision 1.193 2007/12/17 23:42:36 cheshire -Added comments about DNSDigest_SignMessage() - -Revision 1.192 2007/12/17 21:24:09 cheshire -<rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep -We suspend sending of mDNS queries responses when going to sleep, so calculate GetNextScheduledEvent() time accordingly - -Revision 1.191 2007/12/14 00:59:36 cheshire -<rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep -While going to sleep, don't block event scheduling - -Revision 1.190 2007/12/13 20:20:17 cheshire -Minor efficiency tweaks -- converted IdenticalResourceRecord, IdenticalSameNameRecord, and -SameRData from functions to macros, which allows the code to be inlined (the compiler can't -inline a function defined in a different compilation unit) and therefore optimized better. - -Revision 1.189 2007/12/13 00:17:32 cheshire -RDataHashValue was not calculating hash value reliably for RDATA types that have 'holes' in the -in-memory representation (particularly SOA was affected by this, resulting in multiple duplicate -cache entities for the same SOA record, because they had erroneously different rdatahash values). - -Revision 1.188 2007/12/13 00:13:03 cheshire -Simplified RDataHashValue to take a single ResourceRecord pointer, instead of separate rdlength and RDataBody - -Revision 1.187 2007/12/08 00:35:20 cheshire -<rdar://problem/5636422> Updating TXT records is too slow -m->SuppressSending should not suppress all activity, just mDNS Query/Probe/Response - -Revision 1.186 2007/11/15 22:52:29 cheshire -<rdar://problem/5589039> ERROR: mDNSPlatformWriteTCP - send Broken pipe - -Revision 1.185 2007/10/10 20:22:03 cheshire -Added sanity checks in mDNSSendDNSMessage -- we've seen crashes in DNSDigest_SignMessage -apparently caused by trying to sign zero-length messages - -Revision 1.184 2007/10/05 17:56:07 cheshire -Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files - -Revision 1.183 2007/10/02 18:33:46 cheshire -Improved GetRRDisplayString to show all constituent strings within a text record -(up to the usual MaxMsg 120-character limit) - -Revision 1.182 2007/10/01 19:45:01 cheshire -<rdar://problem/5514859> BTMM: Sometimes Back to My Mac autotunnel registrations are malformed - -Revision 1.181 2007/10/01 18:36:53 cheshire -Yet another fix to finally get the DumpPacket RCODE display right - -Revision 1.180 2007/09/29 21:30:38 cheshire -In DumpPacket/DumpRecords, show an error line if we run out of packet data - -Revision 1.179 2007/09/29 20:44:56 cheshire -Fix error in DumpPacket where it was not displaying the RCODE field properly - -Revision 1.178 2007/09/27 21:11:44 cheshire -Fixed spelling mistake: ROCDE -> RCODE - -Revision 1.177 2007/09/27 18:51:26 cheshire -Improved DumpPacket to use "Zone/Prerequisites/Updates" nomenclature when displaying a DNS Update packet - -Revision 1.176 2007/09/27 17:53:37 cheshire -Add display of RCODE and flags in DumpPacket output - -Revision 1.175 2007/09/26 22:26:40 cheshire -Also show DNS query/response ID in DumpPacket output - -Revision 1.174 2007/09/26 16:36:02 cheshire -In DumpPacket output, begin header line with "-- " to make it visually stand out better - -Revision 1.173 2007/09/26 00:49:46 cheshire -Improve packet logging to show sent and received packets, -transport protocol (UDP/TCP/TLS) and source/destination address:port - -Revision 1.172 2007/09/21 23:14:39 cheshire -<rdar://problem/5498009> BTMM: Need to log updates and query packet contents in verbose debug mode -Changed DumpRecords to use LargeCacheRecord on the stack instead of the shared m->rec storage, -to eliminate "GetLargeResourceRecord: m->rec appears to be already in use" warnings - -Revision 1.171 2007/09/21 21:12:36 cheshire -<rdar://problem/5498009> BTMM: Need to log updates and query packet contents - -Revision 1.170 2007/09/07 21:16:58 cheshire -Add new symbol "NATPMPAnnouncementPort" (5350) - -Revision 1.169 2007/08/30 00:31:20 cheshire -Improve "locking failure" debugging messages to show function name using __func__ macro - -Revision 1.168 2007/08/28 23:58:42 cheshire -Rename HostTarget -> AutoTarget - -Revision 1.167 2007/08/10 23:10:05 vazquez -<rdar://problem/5389850> mDNS: Reverse lookups of IPv6 link-local addresses always fail - -Revision 1.166 2007/08/01 16:09:13 cheshire -Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly - -Revision 1.165 2007/08/01 00:04:13 cheshire -<rdar://problem/5261696> Crash in tcpKQSocketCallback -Half-open TCP connections were not being cancelled properly - -Revision 1.164 2007/07/27 20:48:43 cheshire -In DumpRecords(), include record TTL in output - -Revision 1.163 2007/07/16 20:10:11 vazquez -<rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite -Added SSDP port number - -Revision 1.162 2007/07/10 01:59:33 cheshire -<rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks -Fixed GetPktLease to use shared m->rec instead of putting LargeCacheRecord on the stack - -Revision 1.161 2007/07/06 18:56:26 cheshire -Check m->NextScheduledNATOp in GetNextScheduledEvent() - -Revision 1.160 2007/06/29 00:06:42 vazquez -<rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes) - -Revision 1.159 2007/06/28 21:17:17 cheshire -Rename "m->nextevent" as more informative "m->NextuDNSEvent" - -Revision 1.158 2007/05/25 00:25:43 cheshire -<rdar://problem/5227737> Need to enhance putRData to output all current known types - -Revision 1.157 2007/05/23 00:32:15 cheshire -Don't treat uDNS responses as an entire RRSet (kDNSRecordTypePacketUniqueMask) -when received in a truncated UDP response - -Revision 1.156 2007/05/15 00:29:00 cheshire -Print «ZERO ADDRESS» for %#a with a zero mDNSAddr - -Revision 1.155 2007/05/07 22:07:47 cheshire -<rdar://problem/4738025> Enhance GetLargeResourceRecord to decompress more record types - -Revision 1.154 2007/05/04 20:19:53 cheshire -Improve DumpPacket() output - -Revision 1.153 2007/05/01 21:46:31 cheshire -Move GetLLQOptData/GetPktLease from uDNS.c into DNSCommon.c so that dnsextd can use them - -Revision 1.152 2007/04/27 19:28:01 cheshire -Any code that calls StartGetZoneData needs to keep a handle to the structure, so -it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop --- it would start a query and then quickly cancel it, and then when -StartGetZoneData completed, it had a dangling pointer and crashed.) - -Revision 1.151 2007/04/26 13:35:25 cheshire -Add kDNSType_SOA case in SameRDataBody, and a comment in GetLargeResourceRecord about why this is important - -Revision 1.150 2007/04/24 00:17:33 cheshire -Made LocateLLQOptData guard against packets with bogus numAdditionals value - -Revision 1.149 2007/04/23 21:43:00 cheshire -Remove debugging check - -Revision 1.148 2007/04/23 04:55:29 cheshire -Add some defensive null pointer checks - -Revision 1.147 2007/04/22 20:18:10 cheshire -Add comment about mDNSRandom() - -Revision 1.146 2007/04/22 06:02:02 cheshire -<rdar://problem/4615977> Query should immediately return failure when no server - -Revision 1.145 2007/04/20 21:17:24 cheshire -For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative - -Revision 1.144 2007/04/19 18:02:43 cheshire -<rdar://problem/5140504> Unicast DNS response records should tagged with kDNSRecordTypePacketUnique bit - -Revision 1.143 2007/04/16 21:53:49 cheshire -Improve display of negative cache entries - -Revision 1.142 2007/04/05 22:55:35 cheshire -<rdar://problem/5077076> Records are ending up in Lighthouse without expiry information - -Revision 1.141 2007/04/04 01:33:11 cheshire -<rdar://problem/5075200> DNSServiceAddRecord is failing to advertise NULL record -Overly defensive code was zeroing too much of the AuthRecord structure - -Revision 1.140 2007/04/03 19:37:58 cheshire -Rename mDNSAddrIsv4Private() to more precise mDNSAddrIsRFC1918() - -Revision 1.139 2007/04/03 19:18:39 cheshire -Use mDNSSameIPv4Address (and similar) instead of accessing internal fields directly - -Revision 1.138 2007/03/28 21:14:08 cheshire -The rrclass field of an OPT pseudo-RR holds the sender's UDP payload size - -Revision 1.137 2007/03/28 20:59:26 cheshire -<rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr() - -Revision 1.136 2007/03/28 15:56:37 cheshire -<rdar://problem/5085774> Add listing of NAT port mapping and GetAddrInfo requests in SIGINFO output - -Revision 1.135 2007/03/28 01:20:05 cheshire -<rdar://problem/4883206> Improve/create logging for secure browse - -Revision 1.134 2007/03/27 23:25:35 cheshire -Fix error caching SOA records -(cache entry was size of wire-format packed data, not size of in-memory structure) - -Revision 1.133 2007/03/26 22:55:45 cheshire -Add OPT and TSIG to list of types DNSTypeName() knows about - -Revision 1.132 2007/03/22 18:31:48 cheshire -Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy - -Revision 1.131 2007/03/21 21:55:20 cheshire -<rdar://problem/5069688> Hostname gets ; or : which are illegal characters -Error in AppendLabelSuffix() for numbers close to the 32-bit limit - -Revision 1.130 2007/03/21 19:23:37 cheshire -<rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari -Make check less strict so we don't break Bonjour Browser - -Revision 1.129 2007/03/21 01:00:45 cheshire -<rdar://problem/5076826> jmDNS advertised garbage that shows up weird in Safari -DeconstructServiceName() needs to be more defensive about what it considers legal - -Revision 1.128 2007/03/21 00:30:02 cheshire -<rdar://problem/4789455> Multiple errors in DNameList-related code - -Revision 1.127 2007/03/20 17:07:15 cheshire -Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket" - -Revision 1.126 2007/03/10 03:26:44 cheshire -<rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache - -Revision 1.125 2007/03/07 00:08:58 cheshire -<rdar://problem/4347550> Don't allow hyphens at start of service type - -Revision 1.124 2007/01/19 18:04:05 cheshire -For naming consistency, use capital letters for RR types: rdataOpt should be rdataOPT - -Revision 1.123 2007/01/10 22:45:51 cheshire -Cast static strings to "(const domainname*)", not "(domainname*)" - -Revision 1.122 2007/01/06 00:47:35 cheshire -Improve GetRRDisplayString to indicate when record has zero-length rdata - -Revision 1.121 2007/01/05 08:30:39 cheshire -Trim excessive "Log" checkin history from before 2006 -(checkin history still available via "cvs log ..." of course) - -Revision 1.120 2007/01/05 05:23:00 cheshire -Zero DNSQuestion structure in getQuestion (specifically, need TargetQID to be zero'd) - -Revision 1.119 2007/01/05 04:30:16 cheshire -Change a couple of "(domainname *)" casts to "(const domainname *)" - -Revision 1.118 2007/01/04 20:21:59 cheshire -<rdar://problem/4720673> uDNS: Need to start caching unicast records -Don't return multicast answers in response to unicast questions - -Revision 1.117 2006/12/22 20:59:49 cheshire -<rdar://problem/4742742> Read *all* DNS keys from keychain, - not just key for the system-wide default registration domain - -Revision 1.116 2006/12/21 00:04:07 cheshire -To be defensive, put a mDNSPlatformMemZero() at the start of mDNS_SetupResourceRecord() - -Revision 1.115 2006/12/20 04:07:34 cheshire -Remove uDNS_info substructure from AuthRecord_struct - -Revision 1.114 2006/12/19 22:40:04 cheshire -Fix compiler warnings - -Revision 1.113 2006/12/19 02:21:08 cheshire -Delete spurious spaces - -Revision 1.112 2006/12/15 20:42:10 cheshire -<rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records -Additional defensive coding in GetLargeResourceRecord() to reject apparently-valid -rdata that actually runs past the end of the received packet data. - -Revision 1.111 2006/12/15 19:09:57 cheshire -<rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records -Made DomainNameLength() more defensive by adding a limit parameter, so it can be -safely used to inspect potentially malformed data received from external sources. -Without this, a domain name that starts off apparently valid, but extends beyond the end of -the received packet data, could have appeared valid if the random bytes are already in memory -beyond the end of the packet just happened to have reasonable values (e.g. all zeroes). - -Revision 1.110 2006/11/18 05:01:30 cheshire -Preliminary support for unifying the uDNS and mDNS code, -including caching of uDNS answers - -Revision 1.109 2006/11/10 00:54:14 cheshire -<rdar://problem/4816598> Changing case of Computer Name doesn't work - -Revision 1.108 2006/10/05 23:11:18 cheshire -<rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records - -Revision 1.107 2006/09/15 21:20:14 cheshire -Remove uDNS_info substructure from mDNS_struct - -Revision 1.106 2006/08/14 23:24:22 cheshire -Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 - -Revision 1.105 2006/07/15 02:01:28 cheshire -<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder -Fix broken "empty string" browsing - -Revision 1.104 2006/07/05 23:09:13 cheshire -<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder -Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int" - -Revision 1.103 2006/06/29 07:42:14 cheshire -<rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks - -Revision 1.102 2006/06/22 19:49:11 cheshire -Added (commented out) definitions for the LLMNR UDP port and multicast addresses - -Revision 1.101 2006/06/15 21:35:15 cheshire -Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants -from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code - -Revision 1.100 2006/06/08 22:58:46 cheshire -<rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16 - -Revision 1.99 2006/05/18 01:32:33 cheshire -<rdar://problem/4472706> iChat: Lost connection with Bonjour -(mDNSResponder insufficiently defensive against malformed browsing PTR responses) - -Revision 1.98 2006/03/19 17:00:58 cheshire -Define symbol MaxMsg instead of using hard-coded constant value '80' - -Revision 1.97 2006/03/18 21:47:56 cheshire -<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions - -Revision 1.96 2006/03/10 21:51:42 cheshire -<rdar://problem/4111464> After record update, old record sometimes remains in cache -Split out SameRDataBody() into a separate routine so it can be called from other code - -Revision 1.95 2006/03/08 22:43:11 cheshire -Use "localdomain" symbol instead of literal string - -Revision 1.94 2006/03/02 21:59:55 cheshire -<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use" -Improve sanity checks & debugging support in GetLargeResourceRecord() - -Revision 1.93 2006/03/02 20:30:47 cheshire -Improved GetRRDisplayString to also show priority, weight, and port for SRV records - -*/ + */ // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary #define mDNS_InstantiateInlines 1 @@ -580,20 +35,11 @@ Improved GetRRDisplayString to also show priority, weight, and port for SRV reco #pragma mark - Program Constants #endif -mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; -mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; -mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; -mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } }; -mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; -mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; -mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; -mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } }; - -mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } }; - mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; -mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1; -mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)2; +mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1; +mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2; +mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3; +mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4; // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP @@ -606,6 +52,7 @@ mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)2; #define SSHPortAsNumber 22 #define UnicastDNSPortAsNumber 53 #define SSDPPortAsNumber 1900 +#define IPSECPortAsNumber 4500 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback #define NATPMPAnnouncementPortAsNumber 5350 #define NATPMPPortAsNumber 5351 @@ -619,6 +66,7 @@ mDNSexport const mDNSIPPort DiscardPort = { { DiscardPortAsNumber mDNSexport const mDNSIPPort SSHPort = { { SSHPortAsNumber >> 8, SSHPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort SSDPPort = { { SSDPPortAsNumber >> 8, SSDPPortAsNumber & 0xFF } }; +mDNSexport const mDNSIPPort IPSECPort = { { IPSECPortAsNumber >> 8, IPSECPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort NSIPCPort = { { NSIPCPortAsNumber >> 8, NSIPCPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } }; @@ -627,12 +75,26 @@ mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumbe mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } }; -mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; -mDNSexport const mDNSv4Addr AllSystemsMcast = { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements -mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; -//mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR -mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; -//mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR +mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } }; + +mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; +mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; +mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; +mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } }; +mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; +mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; +mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } }; +mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; + +mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; +mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements +mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } }; +mDNSexport const mDNSv6Addr NDP_prefix = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104 +mDNSexport const mDNSEthAddr AllHosts_v6_Eth = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } }; +mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; +//mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR +mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; +//mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } }; @@ -783,7 +245,7 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD break; case kDNSType_NSEC: { - int i; + mDNSu16 i; for (i=0; i<255; i++) if (rd->nsec.bitmap[i>>3] & (128 >> (i&7))) length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(i)); @@ -1274,16 +736,16 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, src = type->c; // Put the service type into the domain name len = *src; - if (len < 2 || len > 15) + if (len < 2 || len > 16) { - LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-14 characters. " + LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. " "See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c); #if APPLE_OSX_mDNSResponder ConvertDomainNameToCString(type, typeBuf); mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, ""); #endif } - if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, &localdomain))) return(mDNSNULL); + if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL); if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; } for (i=2; i<=len; i++) { @@ -1542,6 +1004,7 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD rr->resrec.rrtype = rrtype; rr->resrec.rrclass = kDNSClass_IN; rr->resrec.rroriginalttl = ttl; + rr->resrec.rDNSServer = mDNSNULL; // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal // rr->resrec.rdestimate = set in mDNS_Register_internal // rr->resrec.rdata = MUST be set by client @@ -1583,8 +1046,6 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD rr->Private = 0; rr->updateid = zeroID; rr->zone = rr->resrec.name; - rr->UpdateServer = zeroAddr; - rr->UpdatePort = zeroIPPort; rr->nta = mDNSNULL; rr->tcp = mDNSNULL; rr->OrigRData = 0; @@ -1593,6 +1054,9 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD rr->InFlightRDLen = 0; rr->QueuedRData = 0; rr->QueuedRDLen = 0; + mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); + rr->SRVChanged = mDNSfalse; + rr->mState = mergeState_Zero; rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register() } @@ -1609,6 +1073,7 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I q->ExpectUnique = (qtype != kDNSType_PTR); q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNSfalse; + q->SuppressUnusable = mDNSfalse; q->QuestionCallback = callback; q->QuestionContext = context; } @@ -1667,7 +1132,7 @@ mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr) // r1 has to be a full ResourceRecord including rrtype and rdlength // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1 -mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2) +mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename) { const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data; const RDataBody2 *const b2 = (RDataBody2 *)r2; @@ -1683,26 +1148,26 @@ mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBod b1->soa.retry == b2->soa.retry && b1->soa.expire == b2->soa.expire && b1->soa.min == b2->soa.min && - SameDomainName(&b1->soa.mname, &b2->soa.mname) && - SameDomainName(&b1->soa.rname, &b2->soa.rname)); + samename(&b1->soa.mname, &b2->soa.mname) && + samename(&b1->soa.rname, &b2->soa.rname)); case kDNSType_MX: case kDNSType_AFSDB: case kDNSType_RT: case kDNSType_KX: return(mDNSBool)( b1->mx.preference == b2->mx.preference && - SameDomainName(&b1->mx.exchange, &b2->mx.exchange)); + samename(&b1->mx.exchange, &b2->mx.exchange)); - case kDNSType_RP: return(mDNSBool)( SameDomainName(&b1->rp.mbox, &b2->rp.mbox) && - SameDomainName(&b1->rp.txt, &b2->rp.txt)); + case kDNSType_RP: return(mDNSBool)( samename(&b1->rp.mbox, &b2->rp.mbox) && + samename(&b1->rp.txt, &b2->rp.txt)); case kDNSType_PX: return(mDNSBool)( b1->px.preference == b2->px.preference && - SameDomainName(&b1->px.map822, &b2->px.map822) && - SameDomainName(&b1->px.mapx400, &b2->px.mapx400)); + samename(&b1->px.map822, &b2->px.map822) && + samename(&b1->px.mapx400, &b2->px.mapx400)); case kDNSType_SRV: return(mDNSBool)( b1->srv.priority == b2->srv.priority && b1->srv.weight == b2->srv.weight && mDNSSameIPPort(b1->srv.port, b2->srv.port) && - SameDomainName(&b1->srv.target, &b2->srv.target)); + samename(&b1->srv.target, &b2->srv.target)); case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare @@ -1725,6 +1190,9 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + // Resource record received via unicast, the DNSServer entries should match ? + if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); @@ -1741,7 +1209,14 @@ mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && rr->InterfaceID != q->InterfaceID) return(mDNSfalse); - // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question + // Resource record received via unicast, the DNSServer entries should match ? + if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. + // This also covers the case where the ResourceRecord is mDNSInterface_LocalOnly and the question is expecting a unicast + // DNS response. We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" + // which would then cause other applications (e.g. Safari) to connect to the wrong address. If we decide to support this later, + // the restrictions need to be at least as strict as the restrictions on who can edit /etc/hosts and put fake addresses there. if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. @@ -1757,6 +1232,11 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + // Resource record received via unicast, the DNSServer entries should match ? + // Note that Auth Records are normally setup with NULL InterfaceID and + // both the DNSServers are assumed to be NULL in that case + if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); @@ -1765,6 +1245,20 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); } +// This is called only when the caller knows that it is a Unicast Resource Record and it is a Unicast Question +// and hence we don't need InterfaceID checks like above. Though this may not be a big optimization, the main +// reason we need this is that we can't compare DNSServers between the question and the resource record because +// the resource record may not be completely initialized e.g., mDNSCoreReceiveResponse +mDNSexport mDNSBool UnicastResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) + { + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. + if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); + + if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); + } + mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) { const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data; @@ -1815,10 +1309,11 @@ mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate for (i=sizeof(rdataNSEC); i>0; i--) if (rd->nsec.bitmap[i-1]) break; // For our simplified use of NSEC synthetic records: // nextname is always the record's own name, - // the block number is always 0, - // the count byte is a value in the range 1-32, - // followed by the 1-32 data bytes - return((estimate ? 1 : DomainNameLength(rr->name)) + 2 + i); + // and if we have at least one record type that exists, + // - the block number is always 0, + // - the count byte is a value in the range 1-32, + // - followed by the 1-32 data bytes + return(mDNSu16)((estimate ? 2 : DomainNameLength(rr->name)) + (i ? (2 + i) : 0)); } default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); @@ -2103,7 +1598,7 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS { const int space = DNSOpt_Data_Space(opt); ptr = putVal16(ptr, opt->opt); - ptr = putVal16(ptr, space - 4); + ptr = putVal16(ptr, (mDNSu16)space - 4); switch (opt->opt) { case kDNSOpt_LLQ: @@ -2148,10 +1643,13 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS for (i=sizeof(rdataNSEC); i>0; i--) if (rdb->nsec.bitmap[i-1]) break; ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); if (!ptr) return(mDNSNULL); - if (ptr + 2 + i > limit) return(mDNSNULL); - *ptr++ = 0; - *ptr++ = i; - for (j=0; j<i; j++) *ptr++ = rdb->nsec.bitmap[j]; + if (i) // Only put a block if at least one type exists for this name + { + if (ptr + 2 + i > limit) return(mDNSNULL); + *ptr++ = 0; + *ptr++ = (mDNSu8)i; + for (j=0; j<i; j++) *ptr++ = rdb->nsec.bitmap[j]; + } return ptr; } @@ -2189,6 +1687,8 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 * ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF); ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF); ptr[7] = (mDNSu8)( ttl & 0xFF); + // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes + endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr); if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); } @@ -2264,9 +1764,19 @@ mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecor return ptr; } -mDNSexport mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype) +// for dynamic updates +mDNSexport mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit) + { + // deletion: specify record w/ TTL 0, class NONE + const mDNSu16 origclass = rr->rrclass; + rr->rrclass = kDNSClass_NONE; + ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit); + rr->rrclass = origclass; + return ptr; + } + +mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit) { - const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; mDNSu16 class = kDNSQClass_ANY; ptr = putDomainNameAsLabels(msg, ptr, limit, name); @@ -2317,7 +1827,22 @@ mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) return end; } -mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo) +// for dynamic updates +mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit) + { + AuthRecord rr; + mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + rr.resrec.rrclass = NormalMaxDNSMessageData; + rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + rr.resrec.rdestimate = sizeof(rdataOPT); + rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + rr.resrec.rdata->u.opt[0].u.updatelease = lease; + end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } + return end; + } + +mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo, mDNSu8 *limit) { if (authInfo && authInfo->AutoTunnel) { @@ -2334,7 +1859,7 @@ mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 * mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); hinfo.resrec.rdlength = len; hinfo.resrec.rdestimate = len; - newptr = PutResourceRecord(msg, end, &msg->h.numAdditionals, &hinfo.resrec); + newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit); return newptr; } else @@ -2471,13 +1996,6 @@ mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 return(ptr + pktrdlength); } -mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr) - { - mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]); - *ptr += sizeof(mDNSOpaque16); - return val; - } - mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr) { @@ -2499,7 +2017,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->NextInKAList = mDNSNULL; rr->TimeRcvd = m ? m->timenow : 0; rr->DelayDelivery = 0; - rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTime() + rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord() rr->LastUsed = m ? m->timenow : 0; rr->CRActiveQuestion = mDNSNULL; rr->UnansweredQueries = 0; @@ -2513,8 +2031,11 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->NextInCFList = mDNSNULL; rr->resrec.InterfaceID = InterfaceID; - ptr = getDomainName(msg, ptr, end, &largecr->namestorage); + rr->resrec.rDNSServer = mDNSNULL; + + ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); } + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } @@ -2550,7 +2071,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->resrec.rdlength = 0; else switch (rr->resrec.rrtype) { - case kDNSType_A: if (pktrdlength != sizeof(mDNSv4Addr)) return(mDNSNULL); + case kDNSType_A: if (pktrdlength != sizeof(mDNSv4Addr)) goto fail; rdb->ipv4.b[0] = ptr[0]; rdb->ipv4.b[1] = ptr[1]; rdb->ipv4.b[2] = ptr[2]; @@ -2561,21 +2082,21 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage case kDNSType_CNAME: case kDNSType_PTR: case kDNSType_DNAME:ptr = getDomainName(msg, ptr, end, &rdb->name); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); } + if (ptr != end) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); goto fail; } //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rdb->name.c, pktrdlength); break; case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rdb->soa.mname); - if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; } + if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); goto fail; } ptr = getDomainName(msg, ptr, end, &rdb->soa.rname); - if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; } - if (ptr + 0x14 != end) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); return mDNSNULL; } - rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); - rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); - rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); - rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); - rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); - break; + if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); goto fail; } + if (ptr + 0x14 != end) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); goto fail; } + rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); + rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); + rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); + rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); + rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); + break; case kDNSType_NULL: case kDNSType_HINFO: @@ -2588,7 +2109,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage { debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)", DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); - return(mDNSNULL); + goto fail; } rr->resrec.rdlength = pktrdlength; mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength); @@ -2597,38 +2118,38 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage case kDNSType_MX: case kDNSType_AFSDB: case kDNSType_RT: - case kDNSType_KX: if (pktrdlength < 3) return(mDNSNULL); // Preference + domainname + case kDNSType_KX: if (pktrdlength < 3) goto fail; // Preference + domainname rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); ptr = getDomainName(msg, ptr+2, end, &rdb->mx.exchange); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed MX name"); return(mDNSNULL); } + if (ptr != end) { debugf("GetLargeResourceRecord: Malformed MX name"); goto fail; } //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength); break; case kDNSType_RP: ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox); // Domainname + domainname - if (!ptr) { debugf("GetLargeResourceRecord: Malformed RP mbox"); return mDNSNULL; } + if (!ptr) { debugf("GetLargeResourceRecord: Malformed RP mbox"); goto fail; } ptr = getDomainName(msg, ptr, end, &rdb->rp.txt); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed RP txt"); return mDNSNULL; } + if (ptr != end) { debugf("GetLargeResourceRecord: Malformed RP txt"); goto fail; } break; - case kDNSType_PX: if (pktrdlength < 4) return(mDNSNULL); // Preference + domainname + domainname + case kDNSType_PX: if (pktrdlength < 4) goto fail; // Preference + domainname + domainname rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); ptr = getDomainName(msg, ptr, end, &rdb->px.map822); - if (!ptr) { debugf("GetLargeResourceRecord: Malformed PX map822"); return mDNSNULL; } + if (!ptr) { debugf("GetLargeResourceRecord: Malformed PX map822"); goto fail; } ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); return mDNSNULL; } + if (ptr != end) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); goto fail; } break; - case kDNSType_AAAA: if (pktrdlength != sizeof(mDNSv6Addr)) return(mDNSNULL); + case kDNSType_AAAA: if (pktrdlength != sizeof(mDNSv6Addr)) goto fail; mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6)); break; - case kDNSType_SRV: if (pktrdlength < 7) return(mDNSNULL); // Priority + weight + port + domainname + case kDNSType_SRV: if (pktrdlength < 7) goto fail; // Priority + weight + port + domainname rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); rdb->srv.port.b[0] = ptr[4]; rdb->srv.port.b[1] = ptr[5]; ptr = getDomainName(msg, ptr+6, end, &rdb->srv.target); - if (ptr != end) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); } + if (ptr != end) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); goto fail; } //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength); break; @@ -2637,49 +2158,60 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->resrec.rdlength = 0; while (ptr < end && (mDNSu8 *)(opt+1) < rr->resrec.rdata->u.data + MaximumRDSize) { - if (ptr + 4 > end) { LogMsg("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); return(mDNSNULL); } - opt->opt = getVal16(&ptr); - opt->optlen = getVal16(&ptr); - if (!ValidDNSOpt(opt)) { LogMsg("GetLargeResourceRecord: opt %d optlen %d wrong", opt->opt, opt->optlen); return(mDNSNULL); } - if (ptr + opt->optlen > end) { LogMsg("GetLargeResourceRecord: ptr + opt->optlen > end"); return(mDNSNULL); } - switch(opt->opt) + const rdataOPT *const currentopt = opt; + if (ptr + 4 > end) { LogInfo("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); goto fail; } + opt->opt = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + opt->optlen = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + ptr += 4; + if (ptr + opt->optlen > end) { LogInfo("GetLargeResourceRecord: ptr + opt->optlen > end"); goto fail; } + switch (opt->opt) { case kDNSOpt_LLQ: - opt->u.llq.vers = getVal16(&ptr); - opt->u.llq.llqOp = getVal16(&ptr); - opt->u.llq.err = getVal16(&ptr); - mDNSPlatformMemCopy(opt->u.llq.id.b, ptr, 8); - ptr += 8; - opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); - if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond) - opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond; - ptr += sizeof(mDNSOpaque32); + if (opt->optlen == DNSOpt_LLQData_Space - 4) + { + opt->u.llq.vers = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + opt->u.llq.err = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8); + opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]); + if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond) + opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond; + opt++; + } break; case kDNSOpt_Lease: - opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); - if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond) - opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond; - ptr += sizeof(mDNSs32); + if (opt->optlen == DNSOpt_LeaseData_Space - 4) + { + opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); + if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond) + opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond; + opt++; + } break; case kDNSOpt_Owner: - opt->u.owner.vers = ptr[0]; - opt->u.owner.seq = ptr[1]; - mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address - mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address - opt->u.owner.password = zeroEthAddr; - if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) + if (ValidOwnerLength(opt->optlen)) { - mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address - if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) - mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4)); + opt->u.owner.vers = ptr[0]; + opt->u.owner.seq = ptr[1]; + mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address + mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address + opt->u.owner.password = zeroEthAddr; + if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) + { + mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address + // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above + // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 + if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) + mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4)); + } + opt++; } - ptr += opt->optlen; break; } - opt++; // increment pointer into rdatabody + ptr += currentopt->optlen; } - rr->resrec.rdlength = (mDNSu8*)opt - rr->resrec.rdata->u.data; - if (ptr != end) { LogMsg("GetLargeResourceRecord: Malformed OptRdata"); return(mDNSNULL); } + rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data); + if (ptr != end) { LogInfo("GetLargeResourceRecord: Malformed OptRdata"); goto fail; } break; } @@ -2687,13 +2219,16 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage unsigned int i, j; domainname d; ptr = getDomainName(msg, ptr, end, &d); // Ignored for our simplified use of NSEC synthetic records - if (!ptr) { debugf("GetLargeResourceRecord: Malformed NSEC nextname"); return mDNSNULL; } - if (*ptr++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); return mDNSNULL; } - i = *ptr++; - if (i < 1 || i > sizeof(rdataNSEC)) { debugf("GetLargeResourceRecord: invalid block length %d", i); return mDNSNULL; } + if (!ptr) { LogInfo("GetLargeResourceRecord: Malformed NSEC nextname"); goto fail; } mDNSPlatformMemZero(rdb->nsec.bitmap, sizeof(rdb->nsec.bitmap)); - for (j=0; j<i; j++) rdb->nsec.bitmap[j] = *ptr++; - if (ptr != end) { LogMsg("GetLargeResourceRecord: Malformed NSEC"); return(mDNSNULL); } + if (ptr < end) + { + if (*ptr++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); goto fail; } + i = *ptr++; + if (i > sizeof(rdataNSEC)) { debugf("GetLargeResourceRecord: invalid block length %d", i); goto fail; } + for (j=0; j<i; j++) rdb->nsec.bitmap[j] = *ptr++; + } + if (ptr != end) { debugf("GetLargeResourceRecord: Malformed NSEC"); goto fail; } break; } @@ -2701,7 +2236,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage { debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)", rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); - return(mDNSNULL); + goto fail; } debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data", rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype)); @@ -2715,12 +2250,20 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage break; } - rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us // Success! Now fill in RecordType to show this record contains valid data rr->resrec.RecordType = RecordType; return(end); + +fail: + // If we were unable to parse the rdata in this record, we indicate that by + // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero + rr->resrec.RecordType = kDNSRecordTypePacketNegative; + rr->resrec.rdlength = 0; + rr->resrec.rdestimate = 0; + rr->resrec.rdatahash = 0; + return(end); } mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) @@ -2804,7 +2347,7 @@ mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const if (ptr) { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); - if (ptr) return(&m->rec.r.resrec.rdata->u.opt[0]); + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]); } return(mDNSNULL); } @@ -2833,7 +2376,7 @@ mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, // embedded systems) putting a 9kB object on the stack isn't a big problem. LargeCacheRecord largecr; ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr); - if (ptr) LogMsg("%2d TTL%7d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); + if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); } if (!ptr) LogMsg("ERROR: Premature end of packet data"); return(ptr); @@ -2930,6 +2473,7 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS mStatus status = mStatus_NoError; const mDNSu16 numAdditionals = msg->h.numAdditionals; mDNSu8 *newend; + mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData) @@ -2938,8 +2482,8 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS return mStatus_BadParamErr; } - newend = putHINFO(m, msg, end, authInfo); - if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed"); // Not fatal + newend = putHINFO(m, msg, end, authInfo, limit); + if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal else end = newend; // Put all the integer values in IETF byte-order (MSB first, LSB second) @@ -2985,7 +2529,7 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS #pragma mark - RR List Management & Task Management #endif -mDNSexport void mDNS_Lock_(mDNS *const m) +mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname) { // MUST grab the platform lock FIRST! mDNSPlatformLock(m); @@ -2994,26 +2538,26 @@ mDNSexport void mDNS_Lock_(mDNS *const m) // 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 ForceAlerts if (m->mDNS_busy != m->mDNS_reentrancy) { - LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + LogMsg("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); +#if ForceAlerts *(long*)0 = 0; - } #endif + } // If this is an initial entry into the mDNSCore code, set m->timenow // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set if (m->mDNS_busy == 0) { if (m->timenow) - LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNS_TimeNow_NoLock(m)); + LogMsg("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m)); m->timenow = mDNS_TimeNow_NoLock(m); if (m->timenow == 0) m->timenow = 1; } else if (m->timenow == 0) { - LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); + LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy); m->timenow = mDNS_TimeNow_NoLock(m); if (m->timenow == 0) m->timenow = 1; } @@ -3021,7 +2565,7 @@ mDNSexport void mDNS_Lock_(mDNS *const m) if (m->timenow_last - m->timenow > 0) { m->timenow_adjust += m->timenow_last - m->timenow; - LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust); + LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust); m->timenow = m->timenow_last; } m->timenow_last = m->timenow; @@ -3030,6 +2574,14 @@ mDNSexport void mDNS_Lock_(mDNS *const m) m->mDNS_busy++; } +mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m) + { + AuthRecord *rr; + for (rr = m->NewLocalRecords; rr; rr = rr->next) + if (LocalRecordReady(rr)) return rr; + return mDNSNULL; + } + mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) { mDNSs32 e = m->timenow + 0x78000000; @@ -3039,17 +2591,22 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering; else return(m->timenow); } - if (m->NewLocalOnlyQuestions) return(m->timenow); - if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords)) return(m->timenow); - if (m->SPSProxyListChanged) return(m->timenow); + if (m->NewLocalOnlyQuestions) return(m->timenow); + if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow); + if (m->SPSProxyListChanged) return(m->timenow); + if (m->LocalRemoveEvents) return(m->timenow); + #ifndef UNICAST_DISABLED if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent; if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp; + if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate; #endif + if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; - if (m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; - if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep; + // NextScheduledSPRetry only valid when DelaySleep not set + if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; + if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep; if (m->SuppressSending) { @@ -3081,56 +2638,67 @@ mDNSexport void ShowTaskSchedulingError(mDNS *const m) LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)", m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); - if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords)) - LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, m->NewLocalRecords)); + if (m->NewLocalRecords) + { + AuthRecord *rr = AnyLocalRecordReady(m); + if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr)); + } + + if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged"); + if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents"); if (m->timenow - m->NextScheduledEvent >= 0) LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent); - if (m->SuppressSending && m->timenow - m->SuppressSending >= 0) - LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending); + +#ifndef UNICAST_DISABLED + if (m->timenow - m->NextuDNSEvent >= 0) + LogMsg("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent); + if (m->timenow - m->NextScheduledNATOp >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp); + if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) + LogMsg("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate); +#endif + if (m->timenow - m->NextCacheCheck >= 0) LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck); + if (m->timenow - m->NextScheduledSPS >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS); + if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0) + LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry); + if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) + LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep); + + if (m->SuppressSending && m->timenow - m->SuppressSending >= 0) + LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending); if (m->timenow - m->NextScheduledQuery >= 0) LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery); if (m->timenow - m->NextScheduledProbe >= 0) LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe); if (m->timenow - m->NextScheduledResponse >= 0) LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse); - if (m->timenow - m->NextScheduledNATOp >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp); - if (m->timenow - m->NextScheduledSPS >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS); - if (m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry); - if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) - LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep); -#ifndef UNICAST_DISABLED - if (m->timenow - m->NextuDNSEvent >= 0) - LogMsg("Task Scheduling Error: NextuDNSEvent %d", m->timenow - m->NextuDNSEvent); -#endif mDNS_Unlock(m); } -mDNSexport void mDNS_Unlock_(mDNS *const m) +mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname) { // Decrement mDNS_busy m->mDNS_busy--; // Check for locking failures -#if ForceAlerts if (m->mDNS_busy != m->mDNS_reentrancy) { - LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); +#if ForceAlerts *(long*)0 = 0; - } #endif + } // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow if (m->mDNS_busy == 0) { m->NextScheduledEvent = GetNextScheduledEvent(m); - if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero"); + if (m->timenow == 0) LogMsg("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname); m->timenow = 0; } @@ -3307,7 +2875,7 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt break; case 'p' : F.havePrecision = F.lSize = 1; - F.precision = 8; + F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit case 'X' : digits = "0123456789ABCDEF"; goto hexadecimal; case 'x' : digits = "0123456789abcdef"; diff --git a/external/apache2/mDNSResponder/dist/mDNSCore/DNSDigest.c b/external/apache2/mDNSResponder/dist/mDNSCore/DNSDigest.c index 3dabfa22d27..d3d0a5cdf7c 100644 --- a/external/apache2/mDNSResponder/dist/mDNSCore/DNSDigest.c +++ b/external/apache2/mDNSResponder/dist/mDNSCore/DNSDigest.c @@ -13,99 +13,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - - Change History (most recent first): - -Log: DNSDigest.c,v $ -Revision 1.26 2008/10/10 23:21:51 mcguire -fixed typo in original MD5 source reference - -Revision 1.25 2007/12/17 23:48:29 cheshire -DNSDigest_SignMessage doesn't need to return a result -- it already updates the 'end' parameter - -Revision 1.24 2007/11/30 23:03:51 cheshire -Fixes for EFI: Use "mDNSPlatformMemCopy" instead of assuming existence of "memcpy" - -Revision 1.23 2007/09/21 21:12:36 cheshire -DNSDigest_SignMessage does not need separate "mDNSu16 *numAdditionals" parameter - -Revision 1.22 2007/04/22 06:02:02 cheshire -<rdar://problem/4615977> Query should immediately return failure when no server - -Revision 1.21 2007/03/22 18:31:48 cheshire -Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy - -Revision 1.20 2006/12/22 20:59:49 cheshire -<rdar://problem/4742742> Read *all* DNS keys from keychain, - not just key for the system-wide default registration domain - -Revision 1.19 2006/12/21 00:06:07 cheshire -Don't need to do mDNSPlatformMemZero() -- mDNS_SetupResourceRecord() does it for us - -Revision 1.18 2006/12/19 22:41:21 cheshire -Fix compiler warnings - -Revision 1.17 2006/08/14 23:24:22 cheshire -Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 - -Revision 1.16 2006/07/05 23:05:15 cheshire -<rdar://problem/4472013> Add Private DNS server functionality to dnsextd -Add DNSDigest_VerifyMessage() function - -Revision 1.15 2006/06/20 04:12:30 cheshire -<rdar://problem/4490961> DNS Update broken - -Revision 1.14 2006/02/25 23:12:07 cheshire -<rdar://problem/4427969> Fix to avoid code generation warning/error on FreeBSD 7 - -Revision 1.13 2004/12/16 20:12:59 cheshire -<rdar://problem/3324626> Cache memory management improvements - -Revision 1.12 2004/12/03 07:20:50 ksekar -<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails - -Revision 1.11 2004/12/02 01:10:27 cheshire -Fix to compile cleanly on 64-bit x86 - -Revision 1.10 2004/11/01 20:36:04 ksekar -<rdar://problem/3802395> mDNSResponder should not receive Keychain Notifications - -Revision 1.9 2004/10/26 09:00:12 cheshire -Save a few bytes by creating HMAC_MD5_AlgName as a C string instead of a 256-byte object - -Revision 1.8 2004/09/17 01:08:48 cheshire -Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h - The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces - declared in that file are ONLY appropriate to single-address-space embedded applications. - For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. - -Revision 1.7 2004/08/15 18:36:38 cheshire -Don't use strcpy() and strlen() on "struct domainname" objects; -use AssignDomainName() and DomainNameLength() instead -(A "struct domainname" is a collection of packed pascal strings, not a C string.) - -Revision 1.6 2004/06/02 00:17:46 ksekar -Referenced original OpenSSL license headers in source file description. - -Revision 1.5 2004/05/20 18:37:37 cheshire -Fix compiler warnings - -Revision 1.4 2004/04/22 20:28:20 cheshire -Use existing facility of PutResourceRecordTTL() to update count field for us - -Revision 1.3 2004/04/22 03:05:28 cheshire -kDNSClass_ANY should be kDNSQClass_ANY - -Revision 1.2 2004/04/15 00:51:28 bradley -Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. -Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. - -Revision 1.1 2004/04/14 23:09:28 ksekar -Support for TSIG signed dynamic updates. - - - -*/ + */ #ifdef __cplusplus @@ -524,6 +432,11 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); * * <appro@fy.chalmers.se> */ + /* + * LLVM is more strict about compatibility of types between input & output constraints, + * but we want these to be rotations of 32 bits, not 64, so we explicitly drop the + * most significant bytes by casting to an unsigned int. + */ # if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) # define ROTATE(a,n) ({ register unsigned int ret; \ asm ( \ diff --git a/external/apache2/mDNSResponder/dist/mDNSCore/mDNS.c b/external/apache2/mDNSResponder/dist/mDNSCore/mDNS.c index 83217b0d6d1..5aeba5c51f1 100755 --- a/external/apache2/mDNSResponder/dist/mDNSCore/mDNS.c +++ b/external/apache2/mDNSResponder/dist/mDNSCore/mDNS.c @@ -34,1460 +34,7 @@ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't * understand why variable y is not of type "char*" just proves the point that poor code * layout leads people to unfortunate misunderstandings about how the C language really works.) - - Change History (most recent first): - -Log: mDNS.c,v $ -Revision 1.969.2.1 2009/07/23 23:41:25 cheshire -<rdar://problem/7086623> Sleep Proxy: Ten-second maintenance wake not long enough to reliably get network connectivity - -Revision 1.969 2009/06/30 21:18:19 cheshire -<rdar://problem/7020041> Plugging and unplugging the power cable shouldn't cause a network change event -Additional fixes: -1. Made mDNS_ActivateNetWake_internal and mDNS_DeactivateNetWake_internal more defensive against bad parameters -2. mDNS_DeactivateNetWake_internal also needs to stop any outstanding Sleep Proxy resolve operations - -Revision 1.968 2009/06/29 23:51:09 cheshire -<rdar://problem/6690034> Can't bind to Active Directory - -Revision 1.967 2009/06/27 00:25:27 cheshire -<rdar://problem/6959273> mDNSResponder taking up 13% CPU with 400 KBps incoming bonjour requests -Removed overly-complicate and ineffective multi-packet known-answer snooping code -(Bracketed it with "#if ENABLE_MULTI_PACKET_QUERY_SNOOPING" for now; will delete actual code later) - -Revision 1.966 2009/06/26 01:55:55 cheshire -<rdar://problem/6890712> mDNS: iChat's Buddy photo always appears as the "shadow person" over Bonjour -Additional refinements -- except for the case of explicit queries for record types we don't have (for names we own), -add additional NSEC records only when there's space to do that without having to generate an additional packet - -Revision 1.965 2009/06/24 22:14:21 cheshire -<rdar://problem/6911445> Plugging and unplugging the power cable shouldn't cause a network change event - -Revision 1.964 2009/06/03 23:07:13 cheshire -<rdar://problem/6890712> mDNS: iChat's Buddy photo always appears as the "shadow person" over Bonjour -Large records were not being added in cases where an NSEC record was also required - -Revision 1.963 2009/05/28 00:39:19 cheshire -<rdar://problem/6926465> Sleep is delayed by 10 seconds if BTMM is on -After receiving confirmation of wide-area record deletion, need to schedule another evaluation of whether we're ready to sleep yet - -Revision 1.962 2009/05/19 23:40:37 cheshire -<rdar://problem/6903507> Sleep Proxy: Retransmission logic not working reliably on quiet networks -Added m->NextScheduledSPRetry timer for scheduling Sleep Proxy registration retries - -Revision 1.961 2009/05/19 23:00:43 cheshire -Improved comments and debugging messages - -Revision 1.960 2009/05/13 17:25:33 mkrochma -<rdar://problem/6879926> Should not schedule maintenance wake when machine has no advertised services -Sleep proxy client should only look for services being advertised via Multicast - -Revision 1.959 2009/05/12 23:10:31 cheshire -<rdar://problem/6879926> Should not schedule maintenance wake when machine has no advertised services -Make new routine mDNSCoreHaveAdvertisedServices so daemon.c can tell whether it needs to schedule a maintenance wake - -Revision 1.958 2009/05/12 19:19:20 cheshire -<rdar://problem/6879925> Sleep Proxy delays sleep by ten seconds when logged in to VPN - -Revision 1.957 2009/05/07 23:56:25 cheshire -<rdar://problem/6601427> Retransmit and retry Sleep Proxy Server requests -To get negative answers for our AAAA query we need to set the ReturnIntermed flag on the NetWakeResolve question - -Revision 1.956 2009/05/07 23:46:27 cheshire -<rdar://problem/6601427> Retransmit and retry Sleep Proxy Server requests - -Revision 1.955 2009/05/07 23:40:54 cheshire -Minor code rearrangement in preparation for upcoming changes - -Revision 1.954 2009/05/01 21:28:34 cheshire -<rdar://problem/6721680> AppleConnectAgent's reachability checks delay sleep by 30 seconds -No longer suspend network operations after we've acknowledged that the machine is going to sleep, -because other software may not have yet acknowledged the sleep event, and may be still trying -to do unicast DNS queries or other Bonjour operations. - -Revision 1.953 2009/05/01 19:17:35 cheshire -<rdar://problem/6501561> Sleep Proxy: Reduce the frequency of maintenance wakes: ODD, fans, power - -Revision 1.952 2009/05/01 19:16:45 mcguire -<rdar://problem/6846322> Crash: mDNS_vsnprintf + 1844 - -Revision 1.951 2009/04/28 23:48:19 jessic2 -<rdar://problem/6830541> regservice_callback: instance->request is NULL 0 - -Revision 1.950 2009/04/25 01:17:10 mcguire -Fix spurious TCP connect failures uncovered by <rdar://problem/6729406> PPP doesn't automatically reconnect on wake from sleep - -Revision 1.949 2009/04/25 01:11:02 mcguire -Refactor: create separate function: RestartRecordGetZoneData - -Revision 1.948 2009/04/24 21:25:16 cheshire -<rdar://problem/6601002> Special case Net Assistant port so Apple Remote Desktop doesn't wake up every machine on the network - -Revision 1.947 2009/04/24 19:41:12 mcguire -<rdar://problem/6791775> 4 second delay in DNS response - -Revision 1.946 2009/04/24 19:28:39 mcguire -<rdar://problem/6791775> 4 second delay in DNS response - -Revision 1.945 2009/04/24 00:30:30 cheshire -<rdar://problem/3476350> Return negative answers when host knows authoritatively that no answer exists -Added code to generate and process NSEC records - -Revision 1.944 2009/04/23 22:06:29 cheshire -Added CacheRecord and InterfaceID parameters to MakeNegativeCacheRecord, in preparation for: -<rdar://problem/3476350> Return negative answers when host knows authoritatively that no answer exists - -Revision 1.943 2009/04/22 01:19:56 jessic2 -<rdar://problem/6814585> Daemon: mDNSResponder is logging garbage for error codes because it's using %ld for int 32 - -Revision 1.942 2009/04/21 02:13:29 cheshire -<rdar://problem/5270176> Local hostname changed even though there really isn't a name conflict -Made code less susceptible to being tricked by stale packets echoed back from the network. - -Revision 1.941 2009/04/15 22:22:23 mcguire -<rdar://problem/6768947> uDNS: Treat RCODE 5 (Refused) responses as failures -Additional fix: protect against deref of NULL - -Revision 1.940 2009/04/15 20:42:51 mcguire -<rdar://problem/6768947> uDNS: Treat RCODE 5 (Refused) responses as failures - -Revision 1.939 2009/04/11 00:19:32 jessic2 -<rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically - -Revision 1.938 2009/04/06 23:44:57 cheshire -<rdar://problem/6757838> mDNSResponder thrashing kernel lock in the UDP close path, hurting SPECweb performance - -Revision 1.937 2009/04/04 00:14:49 mcguire -fix logging in BeginSleepProcessing - -Revision 1.936 2009/04/04 00:10:59 mcguire -don't ignore m->SystemWakeOnLANEnabled when going to sleep - -Revision 1.935 2009/04/01 17:50:11 mcguire -cleanup mDNSRandom - -Revision 1.934 2009/03/27 17:17:58 cheshire -Improved "Ignoring suspect uDNS response" debugging message - -Revision 1.933 2009/03/21 02:40:21 cheshire -<rdar://problem/6704514> uDNS: Need to create negative cache entries for "local" SOA - -Revision 1.932 2009/03/20 23:53:03 jessic2 -<rdar://problem/6646228> SIGHUP should restart all in-progress queries - -Revision 1.931 2009/03/18 19:08:15 cheshire -Show old/new sleep sequence numbers in logical order - -Revision 1.930 2009/03/17 23:40:45 cheshire -For now only try the highest-ranked Sleep Proxy; fixed come compiler warnings - -Revision 1.929 2009/03/17 21:55:56 cheshire -Fixed mistake in logic for decided when we're ready to go to sleep - -Revision 1.928 2009/03/17 19:48:12 cheshire -<rdar://problem/6688927> Don't cache negative unicast answers for Multicast DNS names - -Revision 1.927 2009/03/17 01:22:56 cheshire -<rdar://problem/6601427> Sleep Proxy: Retransmit and retry Sleep Proxy Server requests -Initial support for resolving up to three Sleep Proxies in parallel - -Revision 1.926 2009/03/17 01:05:07 mcguire -<rdar://problem/6657640> Reachability fixes on DNS config change - -Revision 1.925 2009/03/13 01:35:36 mcguire -<rdar://problem/6657640> Reachability fixes on DNS config change - -Revision 1.924 2009/03/10 23:45:20 cheshire -Added comments explaining usage of SetSPSProxyListChanged() - -Revision 1.923 2009/03/09 21:53:02 cheshire -<rdar://problem/6650479> Sleep Proxy: Need to stop proxying when it sees an ARP probe from the client - -Revision 1.922 2009/03/09 21:30:17 cheshire -Improved some LogSPS messages; made RestartProbing() subroutine - -Revision 1.921 2009/03/06 22:53:31 cheshire -Don't bother registering with Sleep Proxy if we have no advertised services - -Revision 1.920 2009/03/06 20:08:55 cheshire -<rdar://problem/6601429> Sleep Proxy: Return error responses to clients - -Revision 1.919 2009/03/05 21:54:43 cheshire -Improved "Sleep Proxy Server started / stopped" message - -Revision 1.918 2009/03/04 01:37:14 cheshire -<rdar://problem/6601428> Limit maximum number of records that a Sleep Proxy Server will accept - -Revision 1.917 2009/03/03 23:14:25 cheshire -Got rid of code duplication by making subroutine "SetupOwnerOpt" - -Revision 1.916 2009/03/03 23:04:43 cheshire -For clarity, renamed "MAC" field to "HMAC" (Host MAC, as opposed to Interface MAC) - -Revision 1.915 2009/03/03 22:51:53 cheshire -<rdar://problem/6504236> Sleep Proxy: Waking on same network but different interface will cause conflicts - -Revision 1.914 2009/03/03 00:46:09 cheshire -Additional debugging information in ResolveSimultaneousProbe - -Revision 1.913 2009/02/27 03:08:47 cheshire -<rdar://problem/6547720> Crash while shutting down when "local" is in the user's DNS searchlist - -Revision 1.912 2009/02/27 02:31:28 cheshire -Improved "Record not found in list" debugging message - -Revision 1.911 2009/02/21 01:42:11 cheshire -Updated log messages - -Revision 1.910 2009/02/19 01:50:53 cheshire -Converted some LogInfo messages to LogSPS - -Revision 1.909 2009/02/14 00:04:59 cheshire -Left-justify interface names - -Revision 1.908 2009/02/13 19:40:07 cheshire -Improved alignment of LogSPS messages - -Revision 1.907 2009/02/13 18:16:05 cheshire -Fixed some compile warnings - -Revision 1.906 2009/02/13 06:10:17 cheshire -Convert LogOperation messages to LogInfo - -Revision 1.905 2009/02/12 20:57:24 cheshire -Renamed 'LogAllOperation' switch to 'LogClientOperations'; added new 'LogSleepProxyActions' switch - -Revision 1.904 2009/02/11 02:37:29 cheshire -m->p->SystemWakeForNetworkAccessEnabled renamed to m->SystemWakeOnLANEnabled -Moved code to send goodbye packets from mDNSCoreMachineSleep into BeginSleepProcessing, -so that it happens correctly even when we delay re-sleep due to a very short wakeup. - -Revision 1.903 2009/02/09 23:34:31 cheshire -Additional logging for debugging unknown packets - -Revision 1.902 2009/02/07 05:57:01 cheshire -Fixed debugging log message - -Revision 1.901 2009/02/07 02:57:31 cheshire -<rdar://problem/6084043> Sleep Proxy: Need to adopt IOPMConnection - -Revision 1.900 2009/02/02 21:29:24 cheshire -<rdar://problem/4786302> Implement logic to determine when to send dot-local lookups via Unicast -If Negative response for our special Microsoft Active Directory "local SOA" check has no -SOA record in the authority section, assume we should cache the negative result for 24 hours - -Revision 1.899 2009/01/31 00:37:50 cheshire -When marking cache records for deletion in response to a uDNS response, -make sure InterfaceID matches (i.e. it should be NULL for a uDNS cache record) - -Revision 1.898 2009/01/30 23:49:20 cheshire -Exclude mDNSInterface_Unicast from "InterfaceID ... not currently found" test - -Revision 1.897 2009/01/30 22:04:49 cheshire -Workaround to reduce load on root name servers when caching the SOA record for "." - -Revision 1.896 2009/01/30 22:00:05 cheshire -Made mDNS_StartQuery_internal pay attention to mDNSInterface_Unicast - -Revision 1.895 2009/01/30 17:46:39 cheshire -Improved debugging messages for working out why spurious name conflicts are happening - -Revision 1.894 2009/01/30 00:22:09 cheshire -<rdar://problem/6540743> No announcement after probing & no conflict notice - -Revision 1.893 2009/01/29 22:27:03 mcguire -<rdar://problem/6407429> Cleanup: Logs about Unknown DNS packet type 5450 - -Revision 1.892 2009/01/24 01:38:23 cheshire -Fixed error in logic for targeted queries - -Revision 1.891 2009/01/22 02:14:25 cheshire -<rdar://problem/6515626> Sleep Proxy: Set correct target MAC address, instead of all zeroes - -Revision 1.890 2009/01/22 00:45:02 cheshire -Improved SPS debugging log messages; we are eligible to start answering ARP requests -after we send our first announcement, not after we send our last probe - -Revision 1.889 2009/01/21 03:43:56 mcguire -<rdar://problem/6511765> BTMM: Add support for setting kDNSServiceErr_NATPortMappingDisabled in DynamicStore - -Revision 1.888 2009/01/20 00:27:43 mcguire -<rdar://problem/6305725> when removing a uDNS record, if a dup exists, copy information to it - -Revision 1.887 2009/01/17 05:14:37 cheshire -Convert SendQueries Probe messages to LogSPS messages - -Revision 1.886 2009/01/17 03:43:09 cheshire -Added SPSLogging switch to facilitate Sleep Proxy Server debugging - -Revision 1.885 2009/01/16 22:44:18 cheshire -<rdar://problem/6402123> Sleep Proxy: Begin ARP Announcements sooner - -Revision 1.884 2009/01/16 21:43:52 cheshire -Let InitializeLastAPTime compute the correct interval, instead of having it passed in as a parameter - -Revision 1.883 2009/01/16 21:11:18 cheshire -When purging expired Sleep Proxy records, need to check DuplicateRecords list too - -Revision 1.882 2009/01/16 19:54:28 cheshire -Use symbols "SleepProxyServiceType" and "localdomain" instead of literal strings - -Revision 1.881 2009/01/14 01:38:38 mcguire -<rdar://problem/6492710> Write out DynamicStore per-interface SleepProxyServer info - -Revision 1.880 2009/01/10 01:51:19 cheshire -q->CurrentAnswers not being incremented/decremented when answering a question with a local AuthRecord - -Revision 1.879 2009/01/10 01:43:52 cheshire -Changed misleading function name 'AnsweredLOQ' to more informative 'AnsweredLocalQ' - -Revision 1.878 2009/01/10 01:38:10 cheshire -Changed misleading function name 'AnswerLocalOnlyQuestionWithResourceRecord' to more informative 'AnswerLocalQuestionWithLocalAuthRecord' - -Revision 1.877 2009/01/10 01:36:08 cheshire -Changed misleading function name 'AnswerLocalOnlyQuestions' to more informative 'AnswerAllLocalQuestionsWithLocalAuthRecord' - -Revision 1.876 2009/01/09 22:56:06 cheshire -Don't touch rr after calling mDNS_Deregister_internal -- the memory may have been free'd - -Revision 1.875 2009/01/09 22:54:46 cheshire -When tranferring record from DuplicateRecords list to ResourceRecords list, -need to copy across state of 'Answered Local-Only-Questions' flag - -Revision 1.874 2009/01/07 23:07:24 cheshire -<rdar://problem/6479416> SPS Client not canceling outstanding resolve call before sleeping - -Revision 1.873 2008/12/17 00:18:59 mkrochma -Change some LogMsg to LogOperation before submitting - -Revision 1.872 2008/12/12 01:30:40 cheshire -Update platform-layer BPF filters when we add or remove AddressProxy records - -Revision 1.871 2008/12/10 02:25:31 cheshire -Minor fixes to use of LogClientOperations symbol - -Revision 1.870 2008/12/10 02:11:41 cheshire -ARMv5 compiler doesn't like uncommented stuff after #endif - -Revision 1.869 2008/12/05 02:35:24 mcguire -<rdar://problem/6107390> Write to the DynamicStore when a Sleep Proxy server is available on the network - -Revision 1.868 2008/12/04 21:08:51 mcguire -<rdar://problem/6116863> mDNS: Provide mechanism to disable Multicast advertisements - -Revision 1.867 2008/11/26 21:19:36 cheshire -<rdar://problem/6374334> Sleeping Server should choose the best Sleep Proxy by using advertised metrics - -Revision 1.866 2008/11/26 20:32:46 cheshire -<rdar://problem/6374328> Sleep Proxy: Advertise BSP metrics in service name -Update advertised name when Sleep Proxy "intent" metric changes - -Revision 1.865 2008/11/26 19:49:25 cheshire -Record originally-requested port in sr->NATinfo.IntPort - -Revision 1.864 2008/11/26 19:02:37 cheshire -Don't answer ARP Probes from owner machine as it wakes up and rejoins the network - -Revision 1.863 2008/11/26 03:59:03 cheshire -Wait 30 seconds before starting ARP Announcements - -Revision 1.862 2008/11/25 23:43:07 cheshire -<rdar://problem/5745355> Crashes at ServiceRegistrationGotZoneData + 397 -Made code more defensive to guard against ServiceRegistrationGotZoneData being called with invalid ServiceRecordSet object - -Revision 1.861 2008/11/25 22:46:30 cheshire -For ease of code searching, renamed ZoneData field of ServiceRecordSet_struct from "nta" to "srs_nta" - -Revision 1.860 2008/11/25 05:07:15 cheshire -<rdar://problem/6374328> Advertise Sleep Proxy metrics in service name - -Revision 1.859 2008/11/20 02:07:56 cheshire -<rdar://problem/6387470> Refresh our NAT mappings on wake from sleep - -Revision 1.858 2008/11/20 01:38:36 cheshire -For consistency with other parts of the code, changed code to only check -that the first 4 bytes of MAC address are zero, not the whole 6 bytes. - -Revision 1.857 2008/11/14 22:55:18 cheshire -Fixed log messages - -Revision 1.856 2008/11/14 21:08:28 cheshire -Only put owner option in query packet if we have a non-zero MAC address to put -Only process owner options in received query packets if the MAC address in the option is non-zero - -Revision 1.855 2008/11/14 02:29:54 cheshire -If Sleep Proxy client fails to renew proxy records before they expire, remove them from our m->ResourceRecords list - -Revision 1.854 2008/11/14 00:00:53 cheshire -After client machine wakes up, Sleep Proxy machine need to remove any records -it was temporarily holding as proxy for that client - -Revision 1.853 2008/11/13 19:07:30 cheshire -Added code to put OPT record, containing owner and lease lifetime, into SPS registration packet - -Revision 1.852 2008/11/12 23:23:11 cheshire -Before waking a host, check to see if it has an SRV record advertising -a service on the port in question, and if not, don't bother waking it. - -Revision 1.851 2008/11/12 01:54:15 cheshire -<rdar://problem/6338021> Add domain back to end of _services._dns-sd._udp PTR records -It turns out it is beneficial to have the domain on the end, because it allows better name compression - -Revision 1.850 2008/11/11 01:56:57 cheshire -Improved name conflict log messages - -Revision 1.849 2008/11/06 23:50:43 cheshire -Allow plain (non-SYN) ssh data packets to wake sleeping host - -Revision 1.848 2008/11/05 02:40:28 mkrochma -Change mDNS_SetFQDN syslog mesage to debugf - -Revision 1.847 2008/11/04 23:06:50 cheshire -Split RDataBody union definition into RDataBody and RDataBody2, and removed -SOA from the normal RDataBody union definition, saving 270 bytes per AuthRecord - -Revision 1.846 2008/11/04 22:21:44 cheshire -Changed zone field of AuthRecord_struct from domainname to pointer, saving 252 bytes per AuthRecord - -Revision 1.845 2008/11/03 23:52:05 cheshire -Improved ARP debugging messages to differentiate ARP Announcements from Requests - -Revision 1.844 2008/10/31 23:43:51 cheshire -Fixed compile error in Posix build - -Revision 1.843 2008/10/31 22:55:04 cheshire -Initial support for structured SPS names - -Revision 1.842 2008/10/30 00:12:07 cheshire -Fixed spin when PutSPSRec fails to put a record because it's too big to fit - -Revision 1.841 2008/10/29 23:23:38 cheshire -Refined cache size reporting to go in steps of 1000 when number is above 1000 - -Revision 1.840 2008/10/29 21:34:10 cheshire -Removed some old debugging messages - -Revision 1.839 2008/10/29 21:31:32 cheshire -Five seconds not always enough time for machine to go to sleep -- increased to ten seconds - -Revision 1.838 2008/10/28 18:30:37 cheshire -Added debugging message in mDNSCoreReceiveRawPacket - -Revision 1.837 2008/10/24 23:58:05 cheshire -Wake up for Back to My Mac IPSEC packets, except NAT keepalive packets - -Revision 1.836 2008/10/24 23:18:18 cheshire -If we have a Sleep Proxy Server, don't remove service registrations from the DNS server - -Revision 1.835 2008/10/24 23:07:59 cheshire -Wake SPS client if we receive conflicting mDNS respoonse (record with same name as one of our unique records, but different rdata) - -Revision 1.834 2008/10/24 23:03:24 cheshire -Wake SPS client if we receive a conflicting ARP (some other machine claiming to own that IP address) - -Revision 1.833 2008/10/24 23:01:26 cheshire -To reduce spurious wakeups for now, we'll only wake for incoming TCP SYN packets - -Revision 1.832 2008/10/24 22:58:24 cheshire -For now, since we don't get IPv6 ND or data packets, don't advertise AAAA records for our SPS clients - -Revision 1.831 2008/10/24 22:50:41 cheshire -When waking SPS client, include interface name in syslog message - -Revision 1.830 2008/10/24 20:50:34 cheshire -Use "#if USE_SEPARATE_UDNS_SERVICE_LIST" instead of "#if defined(USE_SEPARATE_UDNS_SERVICE_LIST)" - -Revision 1.829 2008/10/23 23:55:57 cheshire -Fixed some missing "const" declarations - -Revision 1.828 2008/10/23 22:25:56 cheshire -Renamed field "id" to more descriptive "updateid" - -Revision 1.827 2008/10/23 03:06:25 cheshire -Fixed "Waking host" log message - -Revision 1.826 2008/10/22 23:21:30 cheshire -Make sure we have enough bytes before reading into the transport-level header - -Revision 1.825 2008/10/22 22:31:53 cheshire -Log SYN/FIN/RST bits from TCP header, and don't wake for FIN/RST - -Revision 1.824 2008/10/22 20:00:31 cheshire -If we ourselves go to sleep, stop advertising sleep proxy service, then re-advertise after we wake up - -Revision 1.823 2008/10/22 19:55:35 cheshire -Miscellaneous fixes; renamed FindFirstAnswerInCache to FindSPSInCache - -Revision 1.822 2008/10/22 01:41:39 cheshire -Set question->ThisQInterval back to -1 after we cancel our NetWakeResolve - -Revision 1.821 2008/10/22 01:12:53 cheshire -Answer ARP Requests for any IP address we're proxying for - -Revision 1.820 2008/10/21 01:11:11 cheshire -Added mDNSCoreReceiveRawPacket for handling raw packets received by platform layer - -Revision 1.819 2008/10/20 22:16:27 cheshire -Updated comments; increased cache shedding threshold from 3000 to 4000 - -Revision 1.818 2008/10/16 22:01:54 cheshire -Fix last checkin: Should be "ar->resrec.rdata->u.data", not "ar->resrec.rdata.u.data" - -Revision 1.817 2008/10/16 21:40:49 cheshire -Need to set ar->resrec.rdlength correctly before calling mDNS_Register_internal() - -Revision 1.816 2008/10/15 23:12:36 cheshire -On receiving SPS registration from client, broadcast ARP Announcements claiming ownership of that IP address - -Revision 1.815 2008/10/15 20:46:38 cheshire -When transferring records to SPS, include Lease Option - -Revision 1.814 2008/10/15 19:51:27 cheshire -Change "NOTE:" to "Note:" so that BBEdit 9 stops putting those lines into the funtion popup menu - -Revision 1.813 2008/10/15 00:09:23 cheshire -When acting as Sleep Proxy Server, handle DNS Updates received from SPS clients on the network - -Revision 1.812 2008/10/15 00:01:40 cheshire -When going to sleep, discover and resolve SPS, and if successful, transfer records to it - -Revision 1.811 2008/10/14 23:51:57 cheshire -Created new routine GetRDLengthMem() to compute the in-memory storage requirements for particular rdata - -Revision 1.810 2008/10/14 21:37:55 cheshire -Removed unnecessary m->BeSleepProxyServer variable - -Revision 1.809 2008/10/10 23:45:48 cheshire -For ForceMCast records, SetTargetToHostName should use the dot-local multicast hostname, -not a wide-area unicast hostname - -Revision 1.808 2008/10/09 18:59:19 cheshire -Added NetWakeResolve code, removed unused m->SendDeregistrations and m->SendImmediateAnswers - -Revision 1.807 2008/10/07 15:56:58 cheshire -Fixed "unused variable" warnings in non-debug builds - -Revision 1.806 2008/10/04 00:53:37 cheshire -On interfaces that support Wake-On-LAN, browse to discover Sleep Proxy Servers - -Revision 1.805 2008/10/03 18:17:28 cheshire -<rdar://problem/6134215> Sleep Proxy: Mac with Internet Sharing should also offer Sleep Proxy service -Update advertised Sleep Proxy Server name if user changes computer name - -Revision 1.804 2008/10/03 01:26:06 mcguire -<rdar://problem/6266145> mDNS_FinalExit failed to send goodbye for duplicate uDNS records -Put back Duplicate Record check - -Revision 1.803 2008/10/02 23:38:56 mcguire -<rdar://problem/6266145> mDNS_FinalExit failed to send goodbye for duplicate uDNS records - -Revision 1.802 2008/10/02 23:13:48 cheshire -<rdar://problem/6134215> Sleep Proxy: Mac with Internet Sharing should also offer Sleep Proxy service -Need to drop lock before calling "mDNSCoreBeSleepProxyServer(m, mDNSfalse);" - -Revision 1.801 2008/10/02 22:51:04 cheshire -<rdar://problem/6134215> Sleep Proxy: Mac with Internet Sharing should also offer Sleep Proxy service -Added mDNSCoreBeSleepProxyServer() routine to start and stop Sleep Proxy Service - -Revision 1.800 2008/10/02 22:13:15 cheshire -<rdar://problem/6230680> 100ms delay on shutdown -Additional refinement: Also need to clear m->SuppressSending - -Revision 1.799 2008/09/29 20:12:37 cheshire -Rename 'AnswerLocalQuestions' to more descriptive 'AnswerLocalOnlyQuestions' and 'AnsweredLocalQ' to 'AnsweredLOQ' - -Revision 1.798 2008/09/26 19:53:14 cheshire -Fixed locking error: should not call mDNS_Deregister_internal within "mDNS_DropLock" section - -Revision 1.797 2008/09/25 20:40:59 cheshire -<rdar://problem/6245044> Stop using separate m->ServiceRegistrations list -In mDNS_SetFQDN, need to update all AutoTarget SRV records, even if m->MulticastHostname hasn't changed - -Revision 1.796 2008/09/25 20:17:10 cheshire -<rdar://problem/6245044> Stop using separate m->ServiceRegistrations list -Added defensive code to make sure *all* records of a ServiceRecordSet have -completed deregistering before we pass on the mStatus_MemFree message - -Revision 1.795 2008/09/25 00:30:11 cheshire -<rdar://problem/6245044> Stop using separate m->ServiceRegistrations list - -Revision 1.794 2008/09/24 23:48:05 cheshire -Don't need to pass whole ServiceRecordSet reference to GetServiceTarget; -it only needs to access the embedded SRV member of the set - -Revision 1.793 2008/09/23 04:11:53 cheshire -<rdar://problem/6238774> Remove "local" from the end of _services._dns-sd._udp PTR records - -Revision 1.792 2008/09/23 02:30:07 cheshire -Get rid of PutResourceRecordCappedTTL() - -Revision 1.791 2008/09/20 00:34:21 mcguire -<rdar://problem/6129039> BTMM: Add support for WANPPPConnection - -Revision 1.790 2008/09/18 22:46:34 cheshire -<rdar://problem/6230680> 100ms delay on shutdown - -Revision 1.789 2008/09/18 06:15:06 mkrochma -<rdar://problem/6117156> Cleanup: mDNSResponder logging debugging information to console - -Revision 1.788 2008/09/16 21:11:41 cheshire -<rdar://problem/6223969> mDNS: Duplicate TXT record queries being produced by iPhone Remote - -Revision 1.787 2008/09/05 22:53:24 cheshire -Improve "How is rr->resrec.rroriginalttl <= SecsSinceRcvd" debugging message - -Revision 1.786 2008/09/05 22:23:28 cheshire -Moved initialization of "question->LocalSocket" to more logical place - -Revision 1.785 2008/08/14 19:20:55 cheshire -<rdar://problem/6143846> Negative responses over TCP incorrectly rejected - -Revision 1.784 2008/08/13 00:47:53 mcguire -Handle failures when packet logging - -Revision 1.783 2008/07/25 07:09:51 mcguire -<rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning - -Revision 1.782 2008/07/24 20:23:03 cheshire -<rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning - -Revision 1.781 2008/07/18 21:37:35 mcguire -<rdar://problem/5736845> BTMM: alternate SSDP queries between multicast & unicast - -Revision 1.780 2008/07/18 02:24:36 cheshire -<rdar://problem/6041178> Only trigger reconfirm on hostname if both A and AAAA query fail to elicit a response -Additional fix: Don't want to do the ReconfirmAntecedents() stuff if q->RequestUnicast is set (that indicates -we're still on our first or second query after an interface registration or wake from sleep). - -Revision 1.779 2008/07/18 01:05:23 cheshire -<rdar://problem/6041178> Only trigger reconfirm on hostname if both A and AAAA query fail to elicit a response - -Revision 1.778 2008/06/26 17:24:11 mkrochma -<rdar://problem/5450912> BTMM: Stop listening on UDP 5351 for NAT Status Announcements - -Revision 1.777 2008/06/19 01:20:48 mcguire -<rdar://problem/4206534> Use all configured DNS servers - -Revision 1.776 2008/04/17 20:14:14 cheshire -<rdar://problem/5870023> CurrentAnswers/LargeAnswers/UniqueAnswers counter mismatch - -Revision 1.775 2008/03/26 01:53:34 mcguire -<rdar://problem/5820489> Can't resolve via uDNS when an interface is specified - -Revision 1.774 2008/03/17 17:46:08 mcguire -When activating an LLQ, reset all the important state and destroy any tcp connection, -so that everything will be restarted as if the question had just been asked. -Also reset servPort, so that the SOA query will be re-issued. - -Revision 1.773 2008/03/14 22:52:36 mcguire -<rdar://problem/5321824> write status to the DS -Update status when any unicast LLQ is started - -Revision 1.772 2008/03/06 02:48:34 mcguire -<rdar://problem/5321824> write status to the DS - -Revision 1.771 2008/02/26 22:04:44 cheshire -<rdar://problem/5661661> BTMM: Too many members.mac.com SOA queries -Additional fixes -- should not be calling uDNS_CheckCurrentQuestion on a -question while it's still in our 'm->NewQuestions' section of the list - -Revision 1.770 2008/02/22 23:09:02 cheshire -<rdar://problem/5338420> BTMM: Not processing additional records -Refinements: -1. Check rdatahash == namehash, to skip expensive SameDomainName check when possible -2. Once we decide a record is acceptable, we can break out of the loop - -Revision 1.769 2008/02/22 00:00:19 cheshire -<rdar://problem/5338420> BTMM: Not processing additional records - -Revision 1.768 2008/02/19 23:26:50 cheshire -<rdar://problem/5661661> BTMM: Too many members.mac.com SOA queries - -Revision 1.767 2007/12/22 02:25:29 cheshire -<rdar://problem/5661128> Records and Services sometimes not re-registering on wake from sleep - -Revision 1.766 2007/12/15 01:12:27 cheshire -<rdar://problem/5526796> Need to remove active LLQs from server upon question cancellation, on sleep, and on shutdown - -Revision 1.765 2007/12/15 00:18:51 cheshire -Renamed question->origLease to question->ReqLease - -Revision 1.764 2007/12/14 00:49:53 cheshire -Fixed crash in mDNS_StartExit -- the service deregistration loop needs to use -the CurrentServiceRecordSet mechanism to guard against services being deleted, -just like the record deregistration loop uses m->CurrentRecord. - -Revision 1.763 2007/12/13 20:20:17 cheshire -Minor efficiency tweaks -- converted IdenticalResourceRecord, IdenticalSameNameRecord, and -SameRData from functions to macros, which allows the code to be inlined (the compiler can't -inline a function defined in a different compilation unit) and therefore optimized better. - -Revision 1.762 2007/12/13 00:13:03 cheshire -Simplified RDataHashValue to take a single ResourceRecord pointer, instead of separate rdlength and RDataBody - -Revision 1.761 2007/12/13 00:03:31 cheshire -Improved efficiency in IdenticalResourceRecord() by doing SameRData() check before SameDomainName() check - -Revision 1.760 2007/12/08 00:36:19 cheshire -<rdar://problem/5636422> Updating TXT records is too slow -Remove unnecessary delays on announcing record updates, and on processing them on reception - -Revision 1.759 2007/12/07 22:41:29 cheshire -<rdar://problem/5526800> BTMM: Need to clean up registrations on shutdown -Further refinements -- records on the DuplicateRecords list were getting missed on shutdown - -Revision 1.758 2007/12/07 00:45:57 cheshire -<rdar://problem/5526800> BTMM: Need to clean up registrations on shutdown - -Revision 1.757 2007/12/06 00:22:27 mcguire -<rdar://problem/5604567> BTMM: Doesn't work with Linksys WAG300N 1.01.06 (sending from 1026/udp) - -Revision 1.756 2007/12/05 01:52:30 cheshire -<rdar://problem/5624763> BTMM: getaddrinfo_async_start returns EAI_NONAME when resolving BTMM hostname -Delay returning IPv4 address ("A") results for autotunnel names until after we've set up the tunnel (or tried to) - -Revision 1.755 2007/12/03 23:36:45 cheshire -<rdar://problem/5623140> mDNSResponder unicast DNS improvements -Need to check GetServerForName() result is non-null before dereferencing pointer - -Revision 1.754 2007/12/01 01:21:27 jgraessley -<rdar://problem/5623140> mDNSResponder unicast DNS improvements - -Revision 1.753 2007/12/01 00:44:15 cheshire -Fixed compile warnings, e.g. declaration of 'rr' shadows a previous local - -Revision 1.752 2007/11/14 01:10:51 cheshire -Fixed LogOperation() message wording - -Revision 1.751 2007/10/30 23:49:41 cheshire -<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep -LLQ state was not being transferred properly between duplicate questions - -Revision 1.750 2007/10/29 23:58:52 cheshire -<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events -Use standard "if (mDNSIPv4AddressIsOnes(....ExternalAddress))" mechanism to determine whether callback has been invoked yet - -Revision 1.749 2007/10/29 21:28:36 cheshire -Change "Correcting TTL" log message to LogOperation to suppress it in customer build - -Revision 1.748 2007/10/29 20:02:50 cheshire -<rdar://problem/5526813> BTMM: Wide-area records being announced via multicast - -Revision 1.747 2007/10/26 22:53:50 cheshire -Made mDNS_Register_internal and mDNS_Deregister_internal use AuthRecord_uDNS macro -instead of replicating the logic in both places - -Revision 1.746 2007/10/25 22:48:50 cheshire -Added LogOperation message saying when we restart GetZoneData for record and service registrations - -Revision 1.745 2007/10/25 20:48:47 cheshire -For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer' - -Revision 1.744 2007/10/25 20:06:14 cheshire -Don't try to do SOA queries using private DNS (TLS over TCP) queries - -Revision 1.743 2007/10/25 00:12:46 cheshire -<rdar://problem/5496734> BTMM: Need to retry registrations after failures -Retrigger service registrations whenever a new network interface is added - -Revision 1.742 2007/10/24 22:40:06 cheshire -Renamed: RecordRegistrationCallback -> RecordRegistrationGotZoneData -Renamed: ServiceRegistrationZoneDataComplete -> ServiceRegistrationGotZoneData - -Revision 1.741 2007/10/24 00:50:29 cheshire -<rdar://problem/5496734> BTMM: Need to retry registrations after failures -Retrigger record registrations whenever a new network interface is added - -Revision 1.740 2007/10/23 00:38:03 cheshire -When sending uDNS cache expiration query, need to increment rr->UnansweredQueries -or code will spin sending the same cache expiration query repeatedly - -Revision 1.739 2007/10/22 23:46:41 cheshire -<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep -Need to clear question->nta pointer after calling CancelGetZoneData() - -Revision 1.738 2007/10/19 22:08:49 cheshire -<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep -Additional fixes and refinements - -Revision 1.737 2007/10/18 23:06:42 cheshire -<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep -Additional fixes and refinements - -Revision 1.736 2007/10/18 20:23:17 cheshire -Moved SuspendLLQs into mDNS.c, since it's only called from one place - -Revision 1.735 2007/10/18 00:12:34 cheshire -Fixed "unused variable" compiler warning - -Revision 1.734 2007/10/17 22:49:54 cheshire -<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep - -Revision 1.733 2007/10/17 22:37:23 cheshire -<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events - -Revision 1.732 2007/10/17 21:53:51 cheshire -Improved debugging messages; renamed startLLQHandshakeCallback to LLQGotZoneData - -Revision 1.731 2007/10/17 18:37:50 cheshire -<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown -Further refinement: pre-increment m->CurrentRecord before calling mDNS_Deregister_internal() - -Revision 1.730 2007/10/16 21:16:07 cheshire -<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown - -Revision 1.729 2007/10/05 17:56:10 cheshire -Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files - -Revision 1.728 2007/10/04 23:18:14 cheshire -<rdar://problem/5523706> mDNSResponder flooding DNS servers with unreasonable query level - -Revision 1.727 2007/10/04 22:51:57 cheshire -Added debugging LogOperation message to show when we're sending cache expiration queries - -Revision 1.726 2007/10/03 00:14:24 cheshire -Removed write to null to generate stack trace for SetNextQueryTime locking failure - -Revision 1.725 2007/10/02 21:11:08 cheshire -<rdar://problem/5518270> LLQ refreshes don't work, which breaks BTMM browsing - -Revision 1.724 2007/10/02 20:10:23 cheshire -Additional debugging checks on shutdown -- list all records we didn't send a goodbye for, not just the first one - -Revision 1.723 2007/10/02 19:56:54 cheshire -<rdar://problem/5518310> Double-dispose causes crash changing Dynamic DNS hostname - -Revision 1.722 2007/10/01 22:59:46 cheshire -<rdar://problem/5516303> mDNSResponder did not shut down after 20 seconds -Need to shut down NATTraversals on exit - -Revision 1.721 2007/10/01 18:42:07 cheshire -To make packet logs appear in a more intuitive order, dump received packets *before* handling them, not after - -Revision 1.720 2007/09/29 20:40:19 cheshire -<rdar://problem/5513378> Crash in ReissueBlockedQuestions - -Revision 1.719 2007/09/27 22:23:56 cheshire -<rdar://problem/4947392> uDNS: Use SOA to determine TTL for negative answers -Need to clear m->rec.r.resrec.RecordType after we've finished using m->rec - -Revision 1.718 2007/09/27 22:02:33 cheshire -<rdar://problem/5464941> BTMM: Registered records in BTMM don't get removed from server after calling RemoveRecord - -Revision 1.717 2007/09/27 21:21:39 cheshire -Export CompleteDeregistration so it's callable from other files - -Revision 1.716 2007/09/27 02:12:21 cheshire -Updated GrantCacheExtensions degugging message to show new record lifetime - -Revision 1.715 2007/09/27 01:20:06 cheshire -<rdar://problem/5500077> BTMM: Need to refresh LLQs based on lease life and not TTL of response - -Revision 1.714 2007/09/27 00:37:01 cheshire -<rdar://problem/4947392> BTMM: Use SOA to determine TTL for negative answers - -Revision 1.713 2007/09/27 00:25:39 cheshire -Added ttl_seconds parameter to MakeNegativeCacheRecord in preparation for: -<rdar://problem/4947392> uDNS: Use SOA to determine TTL for negative answers - -Revision 1.712 2007/09/26 23:16:58 cheshire -<rdar://problem/5496399> BTMM: Leopard sending excessive LLQ registration requests to .Mac - -Revision 1.711 2007/09/26 22:06:02 cheshire -<rdar://problem/5507399> BTMM: No immediate failure notifications for BTMM names - -Revision 1.710 2007/09/26 00:49:46 cheshire -Improve packet logging to show sent and received packets, -transport protocol (UDP/TCP/TLS) and source/destination address:port - -Revision 1.709 2007/09/21 21:12:36 cheshire -<rdar://problem/5498009> BTMM: Need to log updates and query packet contents - -Revision 1.708 2007/09/20 23:13:37 cheshire -<rdar://problem/4038277> BTMM: Not getting LLQ remove events when logging out of VPN or disconnecting from network -Additional fix: If we have no DNS servers at all, then immediately purge all unicast cache records (including for LLQs) - -Revision 1.707 2007/09/20 02:29:37 cheshire -<rdar://problem/4038277> BTMM: Not getting LLQ remove events when logging out of VPN or disconnecting from network - -Revision 1.706 2007/09/20 01:13:19 cheshire -Export CacheGroupForName so it's callable from other files - -Revision 1.705 2007/09/20 01:12:06 cheshire -Moved HashSlot(X) from mDNS.c to DNSCommon.h so it's usable in other files - -Revision 1.704 2007/09/19 22:47:25 cheshire -<rdar://problem/5490182> Memory corruption freeing a "no such service" service record - -Revision 1.703 2007/09/14 01:46:59 cheshire -Fix Posix build (#ifdef _LEGACY_NAT_TRAVERSAL_ section included a closing curly brace it should not have) - -Revision 1.702 2007/09/13 22:06:46 cheshire -<rdar://problem/5480643> Tully's Free WiFi: DNS fails -Need to accept DNS responses where the query ID field matches, even if the source address does not - -Revision 1.701 2007/09/12 23:22:32 cheshire -<rdar://problem/5476979> Only accept NAT Port Mapping packets from our default gateway - -Revision 1.700 2007/09/12 23:03:08 cheshire -<rdar://problem/5476978> DNSServiceNATPortMappingCreate callback not giving correct interface index - -Revision 1.699 2007/09/12 22:19:28 cheshire -<rdar://problem/5476977> Need to listen for port 5350 NAT-PMP announcements - -Revision 1.698 2007/09/12 22:13:27 cheshire -Remove DynDNSHostNames cleanly on shutdown - -Revision 1.697 2007/09/12 01:44:47 cheshire -<rdar://problem/5475938> Eliminate "Correcting TTL" syslog messages for unicast DNS records - -Revision 1.696 2007/09/12 01:26:08 cheshire -Initialize LastNATReplyLocalTime to timenow, so that gateway uptime checks work more reliably - -Revision 1.695 2007/09/11 19:19:16 cheshire -Correct capitalization of "uPNP" to "UPnP" - -Revision 1.694 2007/09/10 22:06:51 cheshire -Rename uptime => upseconds and LastNATUptime => LastNATupseconds to make it clear these time values are in seconds - -Revision 1.693 2007/09/07 22:24:36 vazquez -<rdar://problem/5466301> Need to stop spewing mDNSResponderHelper logs - -Revision 1.692 2007/09/07 00:12:09 cheshire -<rdar://problem/5466010> Unicast DNS changes broke efficiency fix 3928456 - -Revision 1.691 2007/09/05 22:25:01 vazquez -<rdar://problem/5400521> update_record mDNSResponder leak - -Revision 1.690 2007/09/05 21:48:01 cheshire -<rdar://problem/5385864> BTMM: mDNSResponder flushes wide-area Bonjour records after an hour for a zone. -Now that we're respecting the TTL of uDNS records in the cache, the LLQ maintenance code needs -to update the cache lifetimes of all relevant records every time it successfully renews an LLQ, -otherwise those records will expire and vanish from the cache. - -Revision 1.689 2007/09/05 02:29:06 cheshire -<rdar://problem/5457287> mDNSResponder taking up 100% CPU in ReissueBlockedQuestions -Additional fixes to code implementing "NoAnswer" logic - -Revision 1.688 2007/08/31 22:56:39 cheshire -<rdar://problem/5407080> BTMM: TTLs incorrect on cached BTMM records - -Revision 1.687 2007/08/31 19:53:14 cheshire -<rdar://problem/5431151> BTMM: IPv6 address lookup should not succeed if autotunnel cannot be setup -If AutoTunnel setup fails, the code now generates a fake NXDomain error saying that the requested AAAA record does not exist - -Revision 1.686 2007/08/30 00:01:56 cheshire -Added comment about SetTargetToHostName() - -Revision 1.685 2007/08/29 01:19:24 cheshire -<rdar://problem/5400181> BTMM: Tunneled services do not need NAT port mappings -Set AutoTarget to Target_AutoHostAndNATMAP for non-AutoTunnel wide-area services - -Revision 1.684 2007/08/28 23:58:42 cheshire -Rename HostTarget -> AutoTarget - -Revision 1.683 2007/08/28 23:53:21 cheshire -Rename serviceRegistrationCallback -> ServiceRegistrationZoneDataComplete - -Revision 1.682 2007/08/27 20:28:19 cheshire -Improve "suspect uDNS response" log message - -Revision 1.681 2007/08/24 23:37:23 cheshire -Added debugging message to show when ExtraResourceRecord callback gets invoked - -Revision 1.680 2007/08/24 00:15:19 cheshire -Renamed GetAuthInfoForName() to GetAuthInfoForName_internal() to make it clear that it may only be called with the lock held - -Revision 1.679 2007/08/23 21:47:09 vazquez -<rdar://problem/5427316> BTMM: mDNSResponder sends NAT-PMP packets on public network -make sure we clean up port mappings on base stations by sending a lease value of 0, -and only send NAT-PMP packets on private networks; also save some memory by -not using packet structs in NATTraversals. - -Revision 1.678 2007/08/01 16:09:13 cheshire -Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly - -Revision 1.677 2007/08/01 01:58:24 cheshire -Added RecordType sanity check in mDNS_Register_internal - -Revision 1.676 2007/08/01 00:04:13 cheshire -<rdar://problem/5261696> Crash in tcpKQSocketCallback -Half-open TCP connections were not being cancelled properly - -Revision 1.675 2007/07/31 02:28:35 vazquez -<rdar://problem/3734269> NAT-PMP: Detect public IP address changes and base station reboot - -Revision 1.674 2007/07/31 01:57:23 cheshire -Adding code to respect TTL received in uDNS responses turned out to -expose other problems; backing out change for now. - -Revision 1.673 2007/07/30 23:31:26 cheshire -Code for respecting TTL received in uDNS responses should exclude LLQ-type responses - -Revision 1.672 2007/07/28 01:25:56 cheshire -<rdar://problem/4780038> BTMM: Add explicit UDP event port to LLQ setup request, to fix LLQs not working behind NAT - -Revision 1.671 2007/07/27 22:32:54 cheshire -When processing TTLs in uDNS responses, we'll currently impose a minimum effective TTL -of 2 seconds, or other stuff breaks (e.g. we end up making a negative cache entry). - -Revision 1.670 2007/07/27 20:54:43 cheshire -Fixed code to respect real record TTL received in uDNS responses - -Revision 1.669 2007/07/27 20:09:32 cheshire -Don't need to dump out all received mDNS packets; they're easily viewed using mDNSNetMonitor - -Revision 1.668 2007/07/27 19:58:47 cheshire -Use symbolic names QC_add and QC_rmv instead of mDNStrue/mDNSfalse - -Revision 1.667 2007/07/27 19:52:10 cheshire -Don't increment m->rrcache_active for no-cache add events - -Revision 1.666 2007/07/27 19:30:39 cheshire -Changed mDNSQuestionCallback parameter from mDNSBool to QC_result, -to properly reflect tri-state nature of the possible responses - -Revision 1.665 2007/07/27 18:44:01 cheshire -Rename "AnswerQuestionWithResourceRecord" to more informative "AnswerCurrentQuestionWithResourceRecord" - -Revision 1.664 2007/07/27 18:38:56 cheshire -Rename "uDNS_CheckQuery" to more informative "uDNS_CheckCurrentQuestion" - -Revision 1.663 2007/07/25 03:05:02 vazquez -Fixes for: -<rdar://problem/5338913> LegacyNATTraversal: UPnP heap overflow -<rdar://problem/5338933> LegacyNATTraversal: UPnP stack buffer overflow -and a myriad of other security problems - -Revision 1.662 2007/07/24 20:22:46 cheshire -Make sure all fields of main mDNS object are initialized correctly - -Revision 1.661 2007/07/21 00:54:45 cheshire -<rdar://problem/5344576> Delay IPv6 address callback until AutoTunnel route and policy is configured - -Revision 1.660 2007/07/20 20:00:45 cheshire -"Legacy Browse" is better called "Automatic Browse" - -Revision 1.659 2007/07/20 00:54:18 cheshire -<rdar://problem/4641118> Need separate SCPreferences for per-user .Mac settings - -Revision 1.658 2007/07/18 02:28:57 cheshire -Don't set AutoTunnel settings in uDNS_RegisterService; should be done in GetServiceTarget - -Revision 1.657 2007/07/18 00:57:10 cheshire -<rdar://problem/5303834> Automatically configure IPSec policy when resolving services -Only need to call AddNewClientTunnel() for IPv6 addresses - -Revision 1.656 2007/07/16 23:54:48 cheshire -<rdar://problem/5338850> Crash when removing or changing DNS keys - -Revision 1.655 2007/07/16 20:11:37 vazquez -<rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite -Init LNT stuff and handle SSDP packets - -Revision 1.654 2007/07/12 23:30:23 cheshire -Changed some 'LogOperation' calls to 'debugf' to reduce verbosity in syslog - -Revision 1.653 2007/07/12 02:51:27 cheshire -<rdar://problem/5303834> Automatically configure IPSec policy when resolving services - -Revision 1.652 2007/07/11 23:43:42 cheshire -Rename PurgeCacheResourceRecord to mDNS_PurgeCacheResourceRecord - -Revision 1.651 2007/07/11 22:44:40 cheshire -<rdar://problem/5328801> SIGHUP should purge the cache - -Revision 1.650 2007/07/11 21:34:09 cheshire -<rdar://problem/5304766> Register IPSec tunnel with IPv4-only hostname and create NAT port mappings -Need to hold mDNS_Lock when calling mDNS_AddDynDNSHostName/mDNS_RemoveDynDNSHostName - -Revision 1.649 2007/07/11 02:52:52 cheshire -<rdar://problem/5303807> Register IPv6-only hostname and don't create port mappings for AutoTunnel services -In uDNS_RegisterService, set HostTarget for AutoTunnel services - -Revision 1.648 2007/07/09 23:48:12 cheshire -Add parentheses around bitwise operation for clarity - -Revision 1.647 2007/07/06 21:17:55 cheshire -Initialize m->retryGetAddr to timenow + 0x78000000; - -Revision 1.646 2007/07/06 18:55:49 cheshire -Initialize m->NextScheduledNATOp - -Revision 1.645 2007/06/29 22:55:54 cheshire -Move declaration of DNSServer *s; Fixed incomplete comment. - -Revision 1.644 2007/06/29 00:07:29 vazquez -<rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes) - -Revision 1.643 2007/06/20 01:10:12 cheshire -<rdar://problem/5280520> Sync iPhone changes into main mDNSResponder code - -Revision 1.642 2007/06/15 21:54:50 cheshire -<rdar://problem/4883206> Add packet logging to help debugging private browsing over TLS - -Revision 1.641 2007/05/25 00:30:24 cheshire -When checking for duplicate questions, make sure privacy (or not) status, and long-lived (or not) -status matches. This is particularly important when doing a private query for an SOA record, -which will result in a call StartGetZoneData which does a non-private query for the same SOA record. -If the latter is tagged as a duplicate of the former, then we have deadlock, and neither will complete. - -Revision 1.640 2007/05/25 00:25:44 cheshire -<rdar://problem/5227737> Need to enhance putRData to output all current known types - -Revision 1.639 2007/05/23 00:51:33 cheshire -Increase threshold for shedding cache records from 512 to 3000. The 512 figure was calculated when -each cache entry took about 700 bytes; now they're only 164 bytes. Also, machines have more RAM these -days, and there are more services being advertised using DNS-SD, so it makes sense to cache more. - -Revision 1.638 2007/05/23 00:43:16 cheshire -If uDNS UDP response has TC (truncated) bit set, don't interpret it as being the entire RRSet - -Revision 1.637 2007/05/14 23:53:00 cheshire -Export mDNS_StartQuery_internal and mDNS_StopQuery_internal so they can be called from uDNS.c - -Revision 1.636 2007/05/10 23:27:15 cheshire -Update mDNS_Deregister_internal debugging messages - -Revision 1.635 2007/05/07 20:43:45 cheshire -<rdar://problem/4241419> Reduce the number of queries and announcements - -Revision 1.634 2007/05/04 22:09:08 cheshire -Only do "restarting exponential backoff sequence" for mDNS questions -In mDNS_RegisterInterface, only retrigger mDNS questions -In uDNS_SetupDNSConfig, use ActivateUnicastQuery() instead of just setting q->ThisQInterval directly - -Revision 1.633 2007/05/04 21:45:12 cheshire -Get rid of unused q->RestartTime; Get rid of uDNS_Close (synonym for uDNS_Sleep) - -Revision 1.632 2007/05/04 20:20:50 cheshire -<rdar://problem/5167331> RegisterRecord and RegisterService need to cancel StartGetZoneData -Need to set srs->nta = mDNSNULL; when regState_NoTarget - -Revision 1.631 2007/05/04 00:39:42 cheshire -<rdar://problem/4410011> Eliminate looping SOA lookups -When creating a cascade of negative SOA cache entries, CacheGroup pointer cg needs to be updated -each time round the loop to reference the right CacheGroup for each newly fabricated SOA name - -Revision 1.630 2007/05/03 22:40:38 cheshire -<rdar://problem/4669229> mDNSResponder ignores bogus null target in SRV record - -Revision 1.629 2007/05/03 00:15:51 cheshire -<rdar://problem/4410011> Eliminate looping SOA lookups - -Revision 1.628 2007/05/02 22:21:33 cheshire -<rdar://problem/5167331> RegisterRecord and RegisterService need to cancel StartGetZoneData - -Revision 1.627 2007/04/30 19:29:13 cheshire -Fix display of port number in "Updating DNS Server" message - -Revision 1.626 2007/04/30 04:21:13 cheshire -Can't safely call AnswerLocalQuestions() from within mDNS_Deregister() -- need to defer it until mDNS_Execute time - -Revision 1.625 2007/04/28 01:34:21 cheshire -Fixed crashing bug: We need to update rr->CRActiveQuestion pointers for *all* questions -(Code was explicitly ignoring wide-area unicast questions, leading to stale pointers and crashes) - -Revision 1.624 2007/04/27 21:04:30 cheshire -On network configuration change, need to call uDNS_RegisterSearchDomains - -Revision 1.623 2007/04/27 19:28:01 cheshire -Any code that calls StartGetZoneData needs to keep a handle to the structure, so -it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop --- it would start a query and then quickly cancel it, and then when -StartGetZoneData completed, it had a dangling pointer and crashed.) - -Revision 1.622 2007/04/26 16:09:22 cheshire -mDNS_StopQueryWithRemoves should ignore kDNSRecordTypePacketNegative records - -Revision 1.621 2007/04/26 15:43:22 cheshire -Make sure DNSServer *s is non-null before using value in LogOperation - -Revision 1.620 2007/04/26 13:11:05 cheshire -Fixed crash when logging out of VPN - -Revision 1.619 2007/04/26 00:35:15 cheshire -<rdar://problem/5140339> uDNS: Domain discovery not working over VPN -Fixes to make sure results update correctly when connectivity changes (e.g. a DNS server -inside the firewall may give answers where a public one gives none, and vice versa.) - -Revision 1.618 2007/04/25 19:26:01 cheshire -m->NextScheduledQuery was getting set too early in SendQueries() -Improved "SendQueries didn't send all its queries" debugging message - -Revision 1.617 2007/04/25 17:48:22 cheshire -Update debugging message - -Revision 1.616 2007/04/25 16:38:32 cheshire -If negative cache entry already exists, reactivate it instead of creating a new one - -Revision 1.615 2007/04/25 02:14:38 cheshire -<rdar://problem/4246187> uDNS: Identical client queries should reference a single shared core query -Additional fixes to make LLQs work properly - -Revision 1.614 2007/04/23 21:52:45 cheshire -<rdar://problem/5094009> IPv6 filtering in AirPort base station breaks Wide-Area Bonjour - -Revision 1.613 2007/04/23 04:58:20 cheshire -<rdar://problem/5072548> Crash when setting extremely large TXT records - -Revision 1.612 2007/04/22 20:39:38 cheshire -<rdar://problem/4633194> Add 20 to 120ms random delay to browses - -Revision 1.611 2007/04/22 18:16:29 cheshire -Removed incorrect ActiveQuestion(q) check that was preventing suspended questions from getting reactivated - -Revision 1.610 2007/04/22 06:02:02 cheshire -<rdar://problem/4615977> Query should immediately return failure when no server - -Revision 1.609 2007/04/20 21:17:24 cheshire -For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative - -Revision 1.608 2007/04/20 19:45:31 cheshire -In LogClientOperations mode, dump out unknown DNS packets in their entirety - -Revision 1.607 2007/04/19 23:56:25 cheshire -Don't do cache-flush processing for LLQ answers - -Revision 1.606 2007/04/19 22:50:53 cheshire -<rdar://problem/4246187> Identical client queries should reference a single shared core query - -Revision 1.605 2007/04/19 20:06:41 cheshire -Rename field 'Private' (sounds like a boolean) to more informative 'AuthInfo' (it's a DomainAuthInfo pointer) - -Revision 1.604 2007/04/19 18:03:04 cheshire -Add "const" declaration - -Revision 1.603 2007/04/06 21:00:25 cheshire -Fix log message typo - -Revision 1.602 2007/04/05 22:55:35 cheshire -<rdar://problem/5077076> Records are ending up in Lighthouse without expiry information - -Revision 1.601 2007/04/04 21:48:52 cheshire -<rdar://problem/4720694> Combine unicast authoritative answer list with multicast list - -Revision 1.600 2007/04/04 01:31:33 cheshire -Improve debugging message - -Revision 1.599 2007/04/04 00:03:26 cheshire -<rdar://problem/5089862> DNSServiceQueryRecord is returning kDNSServiceErr_NoSuchRecord for empty rdata - -Revision 1.598 2007/04/03 19:43:16 cheshire -Use mDNSSameIPPort (and similar) instead of accessing internal fields directly - -Revision 1.597 2007/03/31 00:32:32 cheshire -After skipping OPT and TSIG, clear m->rec.r.resrec.RecordType - -Revision 1.596 2007/03/28 20:59:26 cheshire -<rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr() - -Revision 1.595 2007/03/26 23:48:16 cheshire -<rdar://problem/4848295> Advertise model information via Bonjour -Refinements to reduce unnecessary transmissions of the DeviceInfo TXT record - -Revision 1.594 2007/03/26 23:05:05 cheshire -<rdar://problem/5089257> Don't cache TSIG records - -Revision 1.593 2007/03/23 17:40:08 cheshire -<rdar://problem/4060169> Bug when auto-renaming Computer Name after name collision - -Revision 1.592 2007/03/22 18:31:48 cheshire -Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy - -Revision 1.591 2007/03/22 00:49:19 cheshire -<rdar://problem/4848295> Advertise model information via Bonjour - -Revision 1.590 2007/03/21 23:05:59 cheshire -Rename uDNS_HostnameInfo to HostnameInfo; deleted some unused fields - -Revision 1.589 2007/03/20 15:37:19 cheshire -Delete unnecessary log message - -Revision 1.588 2007/03/20 00:24:44 cheshire -<rdar://problem/4175213> Should deliver "name registered" callback slightly *before* announcing PTR record - -Revision 1.587 2007/03/16 22:10:56 cheshire -<rdar://problem/4471307> mDNS: Query for *either* type A or AAAA should return both types - -Revision 1.586 2007/03/10 03:26:44 cheshire -<rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache - -Revision 1.585 2007/03/10 02:02:58 cheshire -<rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache -Eliminate unnecessary "InternalResponseHndlr responseCallback" function pointer - -Revision 1.584 2007/02/28 01:51:27 cheshire -Added comment about reverse-order IP address - -Revision 1.583 2007/01/27 03:19:33 cheshire -Need to initialize question->sock - -Revision 1.582 2007/01/25 00:40:16 cheshire -Unified CNAME-following functionality into cache management code (which means CNAME-following -should now also work for mDNS queries too); deleted defunct pktResponseHndlr() routine. - -Revision 1.581 2007/01/23 02:56:11 cheshire -Store negative results in the cache, instead of generating them out of pktResponseHndlr() - -Revision 1.580 2007/01/19 21:17:33 cheshire -StartLLQPolling needs to call SetNextQueryTime() to cause query to be done in a timely fashion - -Revision 1.579 2007/01/19 18:39:10 cheshire -Fix a bunch of parameters that should have been declared "const" - -Revision 1.578 2007/01/10 22:51:57 cheshire -<rdar://problem/4917539> Add support for one-shot private queries as well as long-lived private queries - -Revision 1.577 2007/01/10 02:05:21 cheshire -Delay uDNS_SetupDNSConfig() until *after* the platform layer -has set up the interface list and security credentials - -Revision 1.576 2007/01/09 02:40:57 cheshire -uDNS_SetupDNSConfig() shouldn't be called from mDNSMacOSX.c (platform support layer); -moved it to mDNS_Init() in mDNS.c (core code) - -Revision 1.575 2007/01/09 00:17:25 cheshire -Improve "ERROR m->CurrentRecord already set" debugging messages - -Revision 1.574 2007/01/05 08:30:41 cheshire -Trim excessive "Log" checkin history from before 2006 -(checkin history still available via "cvs log ..." of course) - -Revision 1.573 2007/01/05 06:34:03 cheshire -Improve "ERROR m->CurrentQuestion already set" debugging messages - -Revision 1.572 2007/01/04 23:11:11 cheshire -<rdar://problem/4720673> uDNS: Need to start caching unicast records -When an automatic browsing domain is removed, generate appropriate "remove" events for legacy queries - -Revision 1.571 2007/01/04 21:45:20 cheshire -Added mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback macros, -to do additional lock sanity checking around callback invocations - -Revision 1.570 2007/01/04 20:57:47 cheshire -Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates) - -Revision 1.569 2007/01/04 20:27:27 cheshire -Change a LogMsg() to debugf() - -Revision 1.568 2007/01/04 02:39:53 cheshire -<rdar://problem/4030599> Hostname passed into DNSServiceRegister ignored for Wide-Area service registrations - -Revision 1.567 2006/12/21 00:01:37 cheshire -Tidy up code alignment - -Revision 1.566 2006/12/20 04:07:34 cheshire -Remove uDNS_info substructure from AuthRecord_struct - -Revision 1.565 2006/12/19 22:49:23 cheshire -Remove uDNS_info substructure from ServiceRecordSet_struct - -Revision 1.564 2006/12/19 02:38:20 cheshire -Get rid of unnecessary duplicate query ID field from DNSQuestion_struct - -Revision 1.563 2006/12/19 02:18:48 cheshire -Get rid of unnecessary duplicate "void *context" field from DNSQuestion_struct - -Revision 1.562 2006/12/16 01:58:31 cheshire -<rdar://problem/4720673> uDNS: Need to start caching unicast records - -Revision 1.561 2006/12/01 07:38:53 herscher -Only perform cache workaround fix if query is wide-area - -Revision 1.560 2006/11/30 23:07:56 herscher -<rdar://problem/4765644> uDNS: Sync up with Lighthouse changes for Private DNS - -Revision 1.559 2006/11/27 08:20:57 cheshire -Preliminary support for unifying the uDNS and mDNS code, including caching of uDNS answers - -Revision 1.558 2006/11/10 07:44:03 herscher -<rdar://problem/4825493> Fix Daemon locking failures while toggling BTMM - -Revision 1.557 2006/11/10 01:12:51 cheshire -<rdar://problem/4829718> Incorrect TTL corrections - -Revision 1.556 2006/11/10 00:54:14 cheshire -<rdar://problem/4816598> Changing case of Computer Name doesn't work - -Revision 1.555 2006/10/30 20:03:37 cheshire -<rdar://problem/4456945> After service restarts on different port, for a few seconds DNS-SD may return stale port number - -Revision 1.554 2006/10/20 05:35:04 herscher -<rdar://problem/4720713> uDNS: Merge unicast active question list with multicast list. - -Revision 1.553 2006/10/05 03:42:43 herscher -Remove embedded uDNS_info struct in DNSQuestion_struct - -Revision 1.552 2006/09/15 21:20:15 cheshire -Remove uDNS_info substructure from mDNS_struct - -Revision 1.551 2006/08/14 23:24:22 cheshire -Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 - -Revision 1.550 2006/07/27 17:58:34 cheshire -Improved text of "SendQueries didn't send all its queries; will try again" debugging message - -Revision 1.549 2006/07/20 22:07:31 mkrochma -<rdar://problem/4633196> Wide-area browsing is currently broken in TOT -More fixes for uninitialized variables - -Revision 1.548 2006/07/20 19:30:19 mkrochma -<rdar://problem/4633196> Wide-area browsing sometimes doesn't work in TOT - -Revision 1.547 2006/07/15 02:31:30 cheshire -<rdar://problem/4630812> Suppress log messages for certain old devices with inconsistent TXT RRSet TTLs - -Revision 1.546 2006/07/07 01:09:09 cheshire -<rdar://problem/4472013> Add Private DNS server functionality to dnsextd -Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsextd - -Revision 1.545 2006/07/05 23:10:30 cheshire -<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder -Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int" - -Revision 1.544 2006/06/29 07:42:14 cheshire -<rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks - -Revision 1.543 2006/06/29 01:38:43 cheshire -<rdar://problem/4605285> Only request unicast responses on wake from sleep and network connection - -Revision 1.542 2006/06/27 23:40:29 cheshire -Fix typo in comment: mis-spelled "compile" - -Revision 1.541 2006/06/27 19:46:24 cheshire -Updated comments and debugging messages - -Revision 1.540 2006/06/15 21:35:16 cheshire -Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants -from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code - -Revision 1.539 2006/06/08 23:45:46 cheshire -Change SimultaneousProbe messages from debugf() to LogOperation() - -Revision 1.538 2006/03/19 17:13:06 cheshire -<rdar://problem/4483117> Need faster purging of stale records -Shorten kDefaultReconfirmTimeForNoAnswer to five seconds -and reconfirm whole chain of antecedents ot once - -Revision 1.537 2006/03/19 02:00:07 cheshire -<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions - -Revision 1.536 2006/03/08 23:29:53 cheshire -<rdar://problem/4468716> Improve "Service Renamed" log message - -Revision 1.535 2006/03/02 20:41:17 cheshire -<rdar://problem/4111464> After record update, old record sometimes remains in cache -Minor code tidying and comments to reduce the risk of similar programming errors in future - -Revision 1.534 2006/03/02 03:25:46 cheshire -<rdar://problem/4111464> After record update, old record sometimes remains in cache -Code to harmonize RRSet TTLs was inadvertently rescuing expiring records - -Revision 1.533 2006/02/26 00:54:41 cheshire -Fixes to avoid code generation warning/error on FreeBSD 7 - -*/ + */ #include "DNSCommon.h" // Defines general DNS untility routines #include "uDNS.h" // Defines entry points into unicast-specific routines @@ -1507,9 +54,27 @@ Fixes to avoid code generation warning/error on FreeBSD 7 #pragma warning(disable:4706) #endif +#if APPLE_OSX_mDNSResponder + +#include <WebFilterDNS/WebFilterDNS.h> + +#if ! NO_WCF +WCFConnection *WCFConnectionNew(void) __attribute__((weak_import)); +void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); + +// Do we really need to define a macro for "if"? +#define CHECK_WCF_FUNCTION(X) if (X) +#endif // ! NO_WCF + +#else + +#define NO_WCF 1 +#endif // APPLE_OSX_mDNSResponder + // Forward declarations mDNSlocal void BeginSleepProcessing(mDNS *const m); mDNSlocal void RetrySPSRegistrations(mDNS *const m); +mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1518,7 +83,6 @@ mDNSlocal void RetrySPSRegistrations(mDNS *const m); #define NO_HINFO 1 -mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; // Any records bigger than this are considered 'large' records #define SmallRecordLimit 1024 @@ -1545,9 +109,6 @@ mDNSexport const char *const mDNS_DomainTypeNames[] = #pragma mark - General Utility Functions #endif -#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) -#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0) - mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) { if (m->mDNS_busy != m->mDNS_reentrancy+1) @@ -1559,14 +120,12 @@ mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) if (ActiveQuestion(q)) { - mDNSs32 sendtime = q->LastQTime + q->ThisQInterval; - - // Don't allow sendtime to be earlier than SuppressStdPort53Queries - if (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived && m->SuppressStdPort53Queries && (sendtime - m->SuppressStdPort53Queries < 0)) - sendtime = m->SuppressStdPort53Queries; - - if (m->NextScheduledQuery - sendtime > 0) - m->NextScheduledQuery = sendtime; + // Depending on whether this is a multicast or unicast question we want to set either: + // m->NextScheduledQuery = NextQSendTime(q) or + // m->NextuDNSEvent = NextQSendTime(q) + mDNSs32 *const timer = mDNSOpaque16IsZero(q->TargetQID) ? &m->NextScheduledQuery : &m->NextuDNSEvent; + if (*timer - NextQSendTime(q) > 0) + *timer = NextQSendTime(q); } } @@ -1584,7 +143,7 @@ mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slo return(CacheGroupForName(m, slot, rr->namehash, rr->name)); } -mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) +mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) { NetworkInterfaceInfo *intf; @@ -1620,16 +179,24 @@ mDNSlocal NetworkInterfaceInfo *FirstInterfaceForID(mDNS *const m, const mDNSInt return(intf); } -mDNSlocal char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID) +mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - return(intf ? intf->ifname : "<NULL InterfaceID>"); + return(intf ? intf->ifname : mDNSNULL); } // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord // Used by AnswerAllLocalQuestionsWithLocalAuthRecord() and AnswerNewLocalOnlyQuestion() mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, QC_result AddRecord) { + // We should not be delivering results for record types Unregistered, Deregistering, and (unverified) Unique + if (!(rr->resrec.RecordType & kDNSRecordTypeActiveMask)) + { + LogMsg("AnswerLocalQuestionWithLocalAuthRecord: *NOT* delivering %s event for local record type %X %s", + AddRecord ? "Add" : "Rmv", rr->resrec.RecordType, ARDisplayString(m, rr)); + return; + } + // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it if (AddRecord) rr->AnsweredLocalQ = mDNStrue; mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback @@ -1641,15 +208,22 @@ mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, DNSQuestion mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again } -// When a new local AuthRecord is created or deleted, AnswerAllLocalQuestionsWithLocalAuthRecord() runs though -// all our local questions (both LocalOnlyQuestions and mDNSInterface_Any questions) delivering answers to each, -// stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion(). -// If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any. -// Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal() +// When a new local AuthRecord is created or deleted, AnswerAllLocalQuestionsWithLocalAuthRecord() +// delivers the appropriate add/remove events to listening questions: +// 1. It runs though all our LocalOnlyQuestions delivering answers as appropriate, +// stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion(). +// 2. If the AuthRecord is marked mDNSInterface_LocalOnly or mDNSInterface_P2P, then it also runs though +// our main question list, delivering answers to mDNSInterface_Any questions as appropriate, +// stopping if it reaches a NewQuestion -- brand-new questions are handled by AnswerNewQuestion(). +// +// AnswerAllLocalQuestionsWithLocalAuthRecord is used by the m->NewLocalRecords loop in mDNS_Execute(), +// and by mDNS_Deregister_internal() + mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) { if (m->CurrentQuestion) - LogMsg("AnswerAllLocalQuestionsWithLocalAuthRecord ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + LogMsg("AnswerAllLocalQuestionsWithLocalAuthRecord ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); m->CurrentQuestion = m->LocalOnlyQuestions; while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions) @@ -1660,8 +234,8 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, AddRecord); // MUST NOT dereference q again } - // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions - if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) + // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P) { m->CurrentQuestion = m->Questions; while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) @@ -1698,6 +272,12 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define InitialAnnounceCount ((mDNSu8)8) +// For goodbye packets we set the count to 3, and for wakeups we set it to 18 +// (which will be up to 15 wakeup attempts over the course of 30 seconds, +// and then if the machine fails to wake, 3 goodbye packets). +#define GoodbyeCount ((mDNSu8)3) +#define WakeupCount ((mDNSu8)18) + // 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. @@ -1706,9 +286,9 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2) #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2) -#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \ - (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \ - (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0) +#define DefaultAPIntervalForRecordType(X) ((X) & kDNSRecordTypeActiveSharedMask ? DefaultAnnounceIntervalForTypeShared : \ + (X) & kDNSRecordTypeUnique ? DefaultProbeIntervalForTypeUnique : \ + (X) & kDNSRecordTypeActiveUniqueMask ? DefaultAnnounceIntervalForTypeUnique : 0) #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0) #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR)) @@ -1792,11 +372,21 @@ mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const r { if (rr->resrec.RecordType == kDNSRecordTypeUnique) { - //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); + if ((rr->LastAPTime + rr->ThisAPInterval) - m->timenow > mDNSPlatformOneSecond * 10) + { + LogMsg("SetNextAnnounceProbeTime: ProbeCount %d Next in %d %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); + LogMsg("SetNextAnnounceProbeTime: m->SuppressProbes %d m->timenow %d diff %d", m->SuppressProbes, m->timenow, m->SuppressProbes - m->timenow); + } if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); + // Some defensive code: + // If (rr->LastAPTime + rr->ThisAPInterval) happens to be far in the past, we don't want to allow + // NextScheduledProbe to be set excessively in the past, because that can cause bad things to happen. + // See: <rdar://problem/7795434> mDNS: Sometimes advertising stops working and record interval is set to zero + if (m->NextScheduledProbe - m->timenow < 0) + m->NextScheduledProbe = m->timenow; } - else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr)) + else if (rr->AnnounceCount && (ResourceRecordIsValidAnswer(rr) || rr->resrec.RecordType == kDNSRecordTypeDeregistering)) { if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); @@ -1808,47 +398,62 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) // For reverse-mapping Sleep Proxy PTR records, probe interval is one second rr->ThisAPInterval = rr->AddressProxy.type ? mDNSPlatformOneSecond : DefaultAPIntervalForRecordType(rr->resrec.RecordType); - // 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 this is a record type that's going to probe, then we use the m->SuppressProbes time. + // * Otherwise, if it's not going to probe, but m->SuppressProbes is set because we have other + // records that are going to probe, then we delay its first announcement so that it will + // go out synchronized with the first announcement for the other records that *are* probing. + // This is a minor performance tweak that helps keep groups of related records synchronized together. + // The addition of "interval / 2" is to make sure that, in the event that any of the probes are + // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. + // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, + // because they will meet the criterion of being at least half-way to their scheduled announcement time. + // * If it's not going to probe and m->SuppressProbes is not already set then we should announce immediately. if (rr->ProbeCount) { // If we have no probe suppression time set, or it is in the past, set it now if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) { - m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique); + // To allow us to aggregate probes when a group of services are registered together, + // the first probe is delayed 1/4 second. This means the common-case behaviour is: + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); + // If we already have a *probe* scheduled to go out sooner, then use that time to get better aggregation if (m->SuppressProbes - m->NextScheduledProbe >= 0) - m->SuppressProbes = m->NextScheduledProbe; + m->SuppressProbes = NonZeroTime(m->NextScheduledProbe); + if (m->SuppressProbes - m->timenow < 0) // Make sure we don't set m->SuppressProbes excessively in the past + m->SuppressProbes = m->timenow; + // If we already have a *query* scheduled to go out sooner, then use that time to get better aggregation if (m->SuppressProbes - m->NextScheduledQuery >= 0) - m->SuppressProbes = m->NextScheduledQuery; + m->SuppressProbes = NonZeroTime(m->NextScheduledQuery); + if (m->SuppressProbes - m->timenow < 0) // Make sure we don't set m->SuppressProbes excessively in the past + m->SuppressProbes = m->timenow; + + // except... don't expect to be able to send before the m->SuppressSending timer fires + if (m->SuppressSending && m->SuppressProbes - m->SuppressSending < 0) + m->SuppressProbes = NonZeroTime(m->SuppressSending); + + if (m->SuppressProbes - m->timenow > mDNSPlatformOneSecond * 8) + { + LogMsg("InitializeLastAPTime ERROR m->SuppressProbes %d m->NextScheduledProbe %d m->NextScheduledQuery %d m->SuppressSending %d %d", + m->SuppressProbes - m->timenow, + m->NextScheduledProbe - m->timenow, + m->NextScheduledQuery - m->timenow, + m->SuppressSending, + m->SuppressSending - m->timenow); + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); + } } + rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; } - - 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 "interval / 2" is to make sure that, in the event that any of the probes are - // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. - // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, - // because they will meet the criterion of being at least half-way to their scheduled announcement time. - if (rr->resrec.RecordType != kDNSRecordTypeUnique) - rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; - - // The exception is unique records that have already been verified and are just being updated - // via mDNS_Update() -- for these we want to announce the new value immediately, without delay. - if (rr->resrec.RecordType == kDNSRecordTypeVerified) + else if (m->SuppressProbes && m->SuppressProbes - m->timenow >= 0) + rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; + else rr->LastAPTime = m->timenow - rr->ThisAPInterval; // For reverse-mapping Sleep Proxy PTR records we don't want to start probing instantly -- we @@ -1859,13 +464,50 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address. if (rr->AddressProxy.type) rr->LastAPTime = m->timenow; - // For now, since we don't yet handle IPv6 ND or data packets, we send deletions for our SPS clients' AAAA records - if (rr->WakeUp.HMAC.l[0] && rr->resrec.rrtype == kDNSType_AAAA) - rr->LastAPTime = m->timenow - rr->ThisAPInterval + mDNSPlatformOneSecond * 10; + // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating, + // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited + // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower. + // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage + // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records. + if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6) + if (rr->WakeUp.HMAC.l[0] && rr->resrec.rrtype == kDNSType_AAAA) + rr->LastAPTime = m->timenow - rr->ThisAPInterval + mDNSPlatformOneSecond * 10; + + // Set LastMCTime to now, to inhibit multicast responses + // (no need to send additional multicast responses when we're announcing anyway) + rr->LastMCTime = m->timenow; + rr->LastMCInterface = mDNSInterfaceMark; SetNextAnnounceProbeTime(m, rr); } +mDNSlocal const domainname *SetUnicastTargetToHostName(mDNS *const m, AuthRecord *rr) + { + const domainname *target; + if (rr->AutoTarget) + { + // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other + // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate, + // with the port number in our advertised SRV record automatically tracking the external mapped port. + DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); + if (!AuthInfo || !AuthInfo->AutoTunnel) rr->AutoTarget = Target_AutoHostAndNATMAP; + } + + target = GetServiceTarget(m, rr); + if (!target || target->c[0] == 0) + { + // defer registration until we've got a target + LogInfo("SetUnicastTargetToHostName No target for %s", ARDisplayString(m, rr)); + rr->state = regState_NoTarget; + return mDNSNULL; + } + else + { + LogInfo("SetUnicastTargetToHostName target %##s for resource record %s", target->c, ARDisplayString(m,rr)); + return target; + } + } + // Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname // Eventually we should unify this with GetServiceTarget() in uDNS.c mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) @@ -1873,12 +515,13 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) domainname *const target = GetRRDomainNameTarget(&rr->resrec); const domainname *newname = &m->MulticastHostname; - if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype); + if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype)); - if (!(rr->ForceMCast || rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&rr->namestorage))) + if (!(rr->ForceMCast || rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P || IsLocalDomain(&rr->namestorage))) { - const domainname *const n = GetServiceTarget(m, rr); + const domainname *const n = SetUnicastTargetToHostName(m, rr); if (n) newname = n; + else { target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; } } if (target && SameDomainName(target, newname)) @@ -1920,19 +563,84 @@ mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr) } } -mDNSlocal void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) +mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) { + // Make sure that we don't activate the SRV record and associated service records, if it is in + // NoTarget state. First time when a service is being instantiated, SRV record may be in NoTarget state. + // We should not activate any of the other reords (PTR, TXT) that are part of the service. When + // the target becomes available, the records will be reregistered. + if (rr->resrec.rrtype != kDNSType_SRV) + { + AuthRecord *srvRR = mDNSNULL; + if (rr->resrec.rrtype == kDNSType_PTR) + srvRR = rr->Additional1; + else if (rr->resrec.rrtype == kDNSType_TXT) + srvRR = rr->DependentOn; + if (srvRR) + { + if (srvRR->resrec.rrtype != kDNSType_SRV) + { + LogMsg("ActivateUnicastRegistration: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR)); + } + else + { + LogInfo("ActivateUnicastRegistration: Found Service Record %s in state %d for %##s (%s)", + ARDisplayString(m, srvRR), srvRR->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + rr->state = srvRR->state; + } + } + } + + if (rr->state == regState_NoTarget) + { + LogInfo("ActivateUnicastRegistration record %s in regState_NoTarget, not activating", ARDisplayString(m, rr)); + return; + } + // When we wake up from sleep, we call ActivateUnicastRegistration. It is possible that just before we went to sleep, + // the service/record was being deregistered. In that case, we should not try to register again. For the cases where + // the records are deregistered due to e.g., no target for the SRV record, we would have returned from above if it + // was already in NoTarget state. If it was in the process of deregistration but did not complete fully before we went + // to sleep, then it is okay to start in Pending state as we will go back to NoTarget state if we don't have a target. + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to DeregPending", ARDisplayString(m, rr), rr->state); + rr->state = regState_DeregPending; + } + else + { + LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to Pending", ARDisplayString(m, rr), rr->state); + rr->state = regState_Pending; + } rr->ProbeCount = 0; rr->AnnounceCount = 0; - rr->ThisAPInterval = 5 * mDNSPlatformOneSecond; // After doubling, first retry will happen after ten seconds + rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; rr->LastAPTime = m->timenow - rr->ThisAPInterval; - rr->state = regState_FetchingZoneData; - rr->uselease = mDNStrue; - } - -// Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified + rr->expire = 0; // Forget about all the leases, start fresh + rr->uselease = mDNStrue; + rr->updateid = zeroID; + rr->SRVChanged = mDNSfalse; + rr->updateError = mStatus_NoError; + // RestartRecordGetZoneData calls this function whenever a new interface gets registered with core. + // The records might already be registered with the server and hence could have NAT state. + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval); + } + +// Two records qualify to be local duplicates if: +// (a) the RecordTypes are the same, or +// (b) one is Unique and the other Verified +// (c) either is in the process of deregistering #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \ - ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified)) + ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified) || \ + ((A)->resrec.RecordType == kDNSRecordTypeDeregistering || (B)->resrec.RecordType == kDNSRecordTypeDeregistering)) + #define RecordIsLocalDuplicate(A,B) \ ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec)) @@ -1945,7 +653,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) AuthRecord **d = &m->DuplicateRecords; if ((mDNSs32)rr->resrec.rroriginalttl <= 0) - { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } + { LogMsg("mDNS_Register_internal: TTL %X should be 1 - 0x7FFFFFFF %s", rr->resrec.rroriginalttl, ARDisplayString(m, rr)); return(mStatus_BadParamErr); } if (!rr->resrec.RecordType) { LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } @@ -1956,7 +664,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) if (m->DivertMulticastAdvertisements && !AuthRecord_uDNS(rr)) { mDNSInterfaceID previousID = rr->resrec.InterfaceID; - if (rr->resrec.InterfaceID == mDNSInterface_Any) rr->resrec.InterfaceID = mDNSInterface_LocalOnly; + if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_LocalOnly; if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); @@ -1994,7 +702,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } // If this resource record is referencing a specific interface, make sure it exists - if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly) + if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); if (!intf) @@ -2060,8 +768,6 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->Private = 0; rr->updateid = zeroID; rr->zone = rr->resrec.name; - rr->UpdateServer = zeroAddr; - rr->UpdatePort = zeroIPPort; rr->nta = mDNSNULL; rr->tcp = mDNSNULL; rr->OrigRData = 0; @@ -2078,8 +784,26 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } + if (rr->AutoTarget) + { SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime(); +#ifndef UNICAST_DISABLED + // If we have no target record yet, SetTargetToHostName will set rr->state == regState_NoTarget + // In this case we leave the record half-formed in the list, and later we'll remove it from the list and re-add it properly. + if (rr->state == regState_NoTarget) + { + // Initialize the target so that we don't crash while logging etc. + domainname *tar = GetRRDomainNameTarget(&rr->resrec); + if (tar) tar->c[0] = 0; + LogInfo("mDNS_Register_internal: record %s in NoTarget state", ARDisplayString(m, rr)); + } +#endif + } else { rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); @@ -2089,11 +813,6 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) if (!ValidateDomainName(rr->resrec.name)) { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } - // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, - // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". - // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. - if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } - // Don't do this until *after* we've set rr->resrec.rdlength if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata)) { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } @@ -2101,7 +820,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(&rr->resrec); - if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P) { // If this is supposed to be unique, make sure we don't have any name conflicts if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) @@ -2119,17 +838,41 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->resrec.RecordType = kDNSRecordTypeDeregistering; rr->resrec.rroriginalttl = 0; rr->ImmedAnswer = mDNSInterfaceMark; + m->LocalRemoveEvents = mDNStrue; m->NextScheduledResponse = m->timenow; } } } + // For uDNS records, we don't support duplicate checks at this time +#ifndef UNICAST_DISABLED + if (AuthRecord_uDNS(rr)) + { + if (!m->NewLocalRecords) m->NewLocalRecords = rr; + // When we called SetTargetToHostName, it may have caused mDNS_Register_internal to be re-entered, appending new + // records to the list, so we now need to update p to advance to the new end to the list before appending our new record. + // Note that for AutoTunnel this should never happen, but this check makes the code future-proof. + while (*p) p=&(*p)->next; + *p = rr; + if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; + rr->ProbeCount = 0; + rr->AnnounceCount = 0; + if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr); + return(mStatus_NoError); // <--- Note: For unicast records, code currently bails out at this point + } +#endif + // Now that we've finished building our new record, make sure it's not identical to one we already have - for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break; + for (r = m->ResourceRecords; r; r=r->next) + if (RecordIsLocalDuplicate(r, rr)) + { + if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0; + else break; + } if (r) { - debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr)); + debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr)); *d = rr; // If the previous copy of this record is already verified unique, // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. @@ -2140,25 +883,21 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } else { - debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr)); + debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr)); if (!m->NewLocalRecords) m->NewLocalRecords = rr; *p = rr; } - if (!AuthRecord_uDNS(rr)) + if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above { // For records that are not going to probe, acknowledge them right away if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) AcknowledgeRecord(m, rr); + + // Adding a record may affect whether or not we should sleep + mDNS_UpdateAllowSleep(m); } -#ifndef UNICAST_DISABLED - else - { - if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; - ActivateUnicastRegistration(m, rr); - } -#endif - + return(mStatus_NoError); } @@ -2184,10 +923,11 @@ mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr) { RData *OldRData = rr->resrec.rdata; + mDNSu16 OldRDLen = rr->resrec.rdlength; SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... if (rr->UpdateCallback) - rr->UpdateCallback(m, rr, OldRData); // ... and let the client know + rr->UpdateCallback(m, rr, OldRData, OldRDLen); // ... and let the client know } // Note: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list. @@ -2219,7 +959,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, if (*d) { AuthRecord *dup = *d; - debugf("Duplicate record %p taking over from %p %##s (%s)", + debugf("mDNS_Register_internal: Duplicate record %p taking over from %p %##s (%s)", dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); *d = dup->next; // Cut replacement record from DuplicateRecords list dup->next = rr->next; // And then... @@ -2238,8 +978,6 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, dup->LastAPTime = rr->LastAPTime; dup->LastMCTime = rr->LastMCTime; dup->LastMCInterface = rr->LastMCInterface; - dup->UpdateServer = rr->UpdateServer; - dup->UpdatePort = rr->UpdatePort; dup->Private = rr->Private; dup->state = rr->state; rr->RequireGoodbye = mDNSfalse; @@ -2254,7 +992,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, while (*p && *p != rr) p=&(*p)->next; // If we found our record on the duplicate list, then make sure we don't send a goodbye for it if (*p) rr->RequireGoodbye = mDNSfalse; - if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", + if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); } @@ -2281,25 +1019,66 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, // actual goodbye packets. #ifndef UNICAST_DISABLED - if (AuthRecord_uDNS(rr) && rr->RequireGoodbye) - { - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } - rr->resrec.RecordType = kDNSRecordTypeDeregistering; - uDNS_DeregisterRecord(m, rr); - // At this point unconditionally we bail out - // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration, - // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration - // process and will complete asynchronously. Either way we don't need to do anything more here. - return(mStatus_NoError); + if (AuthRecord_uDNS(rr)) + { + if (rr->RequireGoodbye) + { + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + m->LocalRemoveEvents = mDNStrue; + uDNS_DeregisterRecord(m, rr); + // At this point unconditionally we bail out + // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration, + // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration + // process and will complete asynchronously. Either way we don't need to do anything more here. + return(mStatus_NoError); + } + // Sometimes the records don't complete proper deregistration i.e., don't wait for a response + // from the server. In that case, if the records have been part of a group update, clear the + // state here. Some recors e.g., AutoTunnel gets reused without ever being completely initialized + rr->updateid = zeroID; + + // We defer cleaning up NAT state only after sending goodbyes. This is important because + // RecordRegistrationGotZoneData guards against creating NAT state if clientContext is non-NULL. + // This happens today when we turn on/off interface where we get multiple network transitions + // and RestartRecordGetZoneData triggers re-registration of the resource records even though + // they may be in Registered state which causes NAT information to be setup multiple times. Defering + // the cleanup here keeps clientContext non-NULL and hence prevents that. Note that cleaning up + // NAT state here takes care of the case where we did not send goodbyes at all. + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + } + if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } + if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } } #endif // UNICAST_DISABLED - if (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->AnsweredLocalQ)) + if (RecordType == kDNSRecordTypeUnregistered) + LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m, rr)); + else if (RecordType == kDNSRecordTypeDeregistering) { - verbosedebugf("mDNS_Deregister_internal: Sending deregister for %s", ARDisplayString(m, rr)); + LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m, rr)); + return(mStatus_BadReferenceErr); + } + + // <rdar://problem/7457925> Local-only questions don't get remove events for unique records + // We may want to consider changing this code so that we generate local-only question "rmv" + // events (and maybe goodbye packets too) for unique records as well as for shared records + // Note: If we change the logic for this "if" statement, need to ensure that the code in + // CompleteDeregistration() sets the appropriate state variables to gaurantee that "else" + // clause will execute here and the record will be cut from the list. + if (rr->WakeUp.HMAC.l[0] || + (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->AnsweredLocalQ))) + { + verbosedebugf("mDNS_Deregister_internal: Starting deregistration for %s", ARDisplayString(m, rr)); rr->resrec.RecordType = kDNSRecordTypeDeregistering; rr->resrec.rroriginalttl = 0; - rr->ImmedAnswer = mDNSInterfaceMark; + rr->AnnounceCount = rr->WakeUp.HMAC.l[0] ? WakeupCount : (drt == mDNS_Dereg_rapid) ? 1 : GoodbyeCount; + rr->ThisAPInterval = mDNSPlatformOneSecond * 2; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; + m->LocalRemoveEvents = mDNStrue; if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0) m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10); } @@ -2311,15 +1090,12 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next; rr->next = mDNSNULL; - if (RecordType == kDNSRecordTypeUnregistered) - LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m, rr)); - else if (RecordType == kDNSRecordTypeDeregistering) - LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m, rr)); - else - { - verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m, rr)); - rr->resrec.RecordType = kDNSRecordTypeUnregistered; - } + // Should we generate local remove events here? + // i.e. something like: + // if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; } + + verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeUnregistered; if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared) debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", @@ -2328,8 +1104,6 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, // If we have an update queued up which never executed, give the client a chance to free that memory if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client - if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; } - if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; } // 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. @@ -2338,6 +1112,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, if (drt != mDNS_Dereg_conflict) { mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + LogInfo("mDNS_Deregister_internal: mStatus_MemFree for %s", ARDisplayString(m, rr)); if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again @@ -2360,6 +1135,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, } } } + mDNS_UpdateAllowSleep(m); return(mStatus_NoError); } @@ -2496,22 +1272,29 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d rr->NR_AdditionalTo = mDNSNULL; } - if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL); + if (m->omsg.h.numAnswers) + mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL); } } +// CompleteDeregistration guarantees that on exit the record will have been cut from the m->ResourceRecords list +// and the client's mStatus_MemFree callback will have been invoked mDNSexport void CompleteDeregistration(mDNS *const m, AuthRecord *rr) { + LogInfo("CompleteDeregistration: called for Resource record %s", ARDisplayString(m, rr)); // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() that // it should go ahead and immediately dispose of this registration rr->resrec.RecordType = kDNSRecordTypeShared; rr->RequireGoodbye = mDNSfalse; + rr->WakeUp.HMAC = zeroEthAddr; if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; } mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this } -// Note: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change -// the record list and/or question list. +// DiscardDeregistrations is used on shutdown and sleep to discard (forcibly and immediately) +// any deregistering records that remain in the m->ResourceRecords list. +// DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, +// which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void DiscardDeregistrations(mDNS *const m) { @@ -2522,7 +1305,7 @@ mDNSlocal void DiscardDeregistrations(mDNS *const m) while (m->CurrentRecord) { AuthRecord *rr = m->CurrentRecord; - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + if (!AuthRecord_uDNS(rr) && rr->resrec.RecordType == kDNSRecordTypeDeregistering) CompleteDeregistration(m, rr); // Don't touch rr after this else m->CurrentRecord = rr->next; @@ -2539,7 +1322,7 @@ mDNSlocal mStatus GetLabelDecimalValue(const mDNSu8 *const src, mDNSu8 *dst) val = val * 10 + src[i] - '0'; } if (val > 255) return(mStatus_Invalid); - *dst = val; + *dst = (mDNSu8)val; return(mStatus_NoError); } @@ -2579,7 +1362,7 @@ mDNSlocal mStatus GetIPv6FromName(mDNSAddr *const a, const domainname *const nam n = (const domainname *)(n->c + 2); if (l<0 || h<0) return mStatus_Invalid; - a->ip.v6.b[15-i] = (h << 4) | l; + a->ip.v6.b[15-i] = (mDNSu8)((h << 4) | l); } a->type = mDNSAddrType_IPv6; @@ -2599,7 +1382,7 @@ mDNSlocal mDNSs32 ReverseMapDomainType(const domainname *const name) } mDNSlocal void SendARP(mDNS *const m, const mDNSu8 op, const AuthRecord *const rr, - const mDNSu8 *const spa, const mDNSu8 *const tha, const mDNSu8 *const tpa, const mDNSu8 *const dst) + const mDNSv4Addr *const spa, const mDNSEthAddr *const tha, const mDNSv4Addr *const tpa, const mDNSEthAddr *const dst) { int i; mDNSu8 *ptr = m->omsg.data; @@ -2607,10 +1390,10 @@ mDNSlocal void SendARP(mDNS *const m, const mDNSu8 op, const AuthRecord *const r if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; } // 0x00 Destination address - for (i=0; i<6; i++) *ptr++ = dst[i]; + for (i=0; i<6; i++) *ptr++ = dst->b[i]; - // 0x06 Source address (we just use zero -- driver/hardware will fill in real interface address) - for (i=0; i<6; i++) *ptr++ = 0x0; + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0]; // 0x0C ARP Ethertype (0x0806) *ptr++ = 0x08; *ptr++ = 0x06; @@ -2626,18 +1409,131 @@ mDNSlocal void SendARP(mDNS *const m, const mDNSu8 op, const AuthRecord *const r for (i=0; i<6; i++) *ptr++ = intf->MAC.b[i]; // 0x1C Sender protocol address - for (i=0; i<4; i++) *ptr++ = spa[i]; + for (i=0; i<4; i++) *ptr++ = spa->b[i]; // 0x20 Target hardware address - for (i=0; i<6; i++) *ptr++ = tha[i]; + for (i=0; i<6; i++) *ptr++ = tha->b[i]; // 0x26 Target protocol address - for (i=0; i<4; i++) *ptr++ = tpa[i]; + for (i=0; i<4; i++) *ptr++ = tpa->b[i]; // 0x2A Total ARP Packet length 42 bytes mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); } +mDNSlocal mDNSu16 CheckSum(const void *const data, mDNSs32 length, mDNSu32 sum) + { + const mDNSu16 *ptr = data; + while (length > 0) { length -= 2; sum += *ptr++; } + sum = (sum & 0xFFFF) + (sum >> 16); + sum = (sum & 0xFFFF) + (sum >> 16); + return(sum != 0xFFFF ? sum : 0); + } + +mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *const dst, const mDNSu8 protocol, const void *const data, const mDNSu32 length) + { + IPv6PseudoHeader ph; + ph.src = *src; + ph.dst = *dst; + ph.len.b[0] = length >> 24; + ph.len.b[1] = length >> 16; + ph.len.b[2] = length >> 8; + ph.len.b[3] = length; + ph.pro.b[0] = 0; + ph.pro.b[1] = 0; + ph.pro.b[2] = 0; + ph.pro.b[3] = protocol; + return CheckSum(&ph, sizeof(ph), CheckSum(data, length, 0)); + } + +mDNSlocal void SendNDP(mDNS *const m, const mDNSu8 op, const mDNSu8 flags, const AuthRecord *const rr, + const mDNSv6Addr *const spa, const mDNSEthAddr *const tha, const mDNSv6Addr *const tpa, const mDNSEthAddr *const dst) + { + int i; + mDNSOpaque16 checksum; + mDNSu8 *ptr = m->omsg.data; + // Some recipient hosts seem to ignore Neighbor Solicitations if the IPv6-layer destination address is not the + // appropriate IPv6 solicited node multicast address, so we use that IPv6-layer destination address, even though + // at the Ethernet-layer we unicast the packet to the intended target, to avoid wasting network bandwidth. + const mDNSv6Addr mc = { { 0xFF,0x02,0x00,0x00, 0,0,0,0, 0,0,0,1, 0xFF,tpa->b[0xD],tpa->b[0xE],tpa->b[0xF] } }; + const mDNSv6Addr *const v6dst = (op == NDP_Sol) ? &mc : tpa; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID); + if (!intf) { LogMsg("SendNDP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; } + + // 0x00 Destination address + for (i=0; i<6; i++) *ptr++ = dst->b[i]; + // Right now we only send Neighbor Solicitations to verify whether the host we're proxying for has gone to sleep yet. + // Since we know who we're looking for, we send it via Ethernet-layer unicast, rather than bothering every host on the + // link with a pointless link-layer multicast. + // Should we want to send traditional Neighbor Solicitations in the future, where we really don't know in advance what + // Ethernet-layer address we're looking for, we'll need to send to the appropriate Ethernet-layer multicast address: + // *ptr++ = 0x33; + // *ptr++ = 0x33; + // *ptr++ = 0xFF; + // *ptr++ = tpa->b[0xD]; + // *ptr++ = tpa->b[0xE]; + // *ptr++ = tpa->b[0xF]; + + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; + + // 0x0C IPv6 Ethertype (0x86DD) + *ptr++ = 0x86; *ptr++ = 0xDD; + + // 0x0E IPv6 header + *ptr++ = 0x60; *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; // Version, Traffic Class, Flow Label + *ptr++ = 0x00; *ptr++ = 0x20; // Length + *ptr++ = 0x3A; // Protocol == ICMPv6 + *ptr++ = 0xFF; // Hop Limit + + // 0x16 Sender IPv6 address + for (i=0; i<16; i++) *ptr++ = spa->b[i]; + + // 0x26 Destination IPv6 address + for (i=0; i<16; i++) *ptr++ = v6dst->b[i]; + + // 0x36 NDP header + *ptr++ = op; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement + *ptr++ = 0x00; // Code + *ptr++ = 0x00; *ptr++ = 0x00; // Checksum placeholder (0x38, 0x39) + *ptr++ = flags; + *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; + + if (op == NDP_Sol) // Neighbor Solicitation. The NDP "target" is the address we seek. + { + // 0x3E NDP target. + for (i=0; i<16; i++) *ptr++ = tpa->b[i]; + // 0x4E Source Link-layer Address + // <http://www.ietf.org/rfc/rfc2461.txt> + // MUST NOT be included when the source IP address is the unspecified address. + // Otherwise, on link layers that have addresses this option MUST be included + // in multicast solicitations and SHOULD be included in unicast solicitations. + if (!mDNSIPv6AddressIsZero(*spa)) + { + *ptr++ = NDP_SrcLL; // Option Type 1 == Source Link-layer Address + *ptr++ = 0x01; // Option length 1 (in units of 8 octets) + for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; + } + } + else // Neighbor Advertisement. The NDP "target" is the address we're giving information about. + { + // 0x3E NDP target. + for (i=0; i<16; i++) *ptr++ = spa->b[i]; + // 0x4E Target Link-layer Address + *ptr++ = NDP_TgtLL; // Option Type 2 == Target Link-layer Address + *ptr++ = 0x01; // Option length 1 (in units of 8 octets) + for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i]; + } + + // 0x4E or 0x56 Total NDP Packet length 78 or 86 bytes + m->omsg.data[0x13] = ptr - &m->omsg.data[0x36]; // Compute actual length + checksum.NotAnInteger = ~IPv6CheckSum(spa, v6dst, 0x3A, &m->omsg.data[0x36], m->omsg.data[0x13]); + m->omsg.data[0x38] = checksum.b[0]; + m->omsg.data[0x39] = checksum.b[1]; + + mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); + } + mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *const intf, rdataOPT *const owner) { owner->u.owner.vers = 0; @@ -2647,8 +1543,9 @@ mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *co owner->u.owner.password = zeroEthAddr; // Don't try to compute the optlen until *after* we've set up the data fields + // Right now the DNSOpt_Owner_Space macro does not depend on the owner->u.owner being set up correctly, but in the future it might owner->opt = kDNSOpt_Owner; - owner->optlen = DNSOpt_Owner_Space(owner) - 4; + owner->optlen = DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) - 4; } mDNSlocal void GrantUpdateCredit(AuthRecord *rr) @@ -2708,30 +1605,61 @@ mDNSlocal void SendResponses(mDNS *const m) for (rr = m->ResourceRecords; rr; rr=rr->next) { while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); - if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr)) + if (TimeToAnnounceThisRecord(rr, m->timenow)) { - if (rr->AddressProxy.type) + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) { - rr->AnnounceCount--; - rr->ThisAPInterval *= 2; - rr->LastAPTime = m->timenow; - if (rr->AddressProxy.type == mDNSAddrType_IPv4) + if (!rr->WakeUp.HMAC.l[0]) { - LogSPS("ARP Announcement %d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); - SendARP(m, 1, rr, rr->AddressProxy.ip.v4.b, zeroEthAddr.b, rr->AddressProxy.ip.v4.b, onesEthAddr.b); + if (rr->AnnounceCount) rr->ImmedAnswer = mDNSInterfaceMark; // Send goodbye packet on all interfaces } - else if (rr->AddressProxy.type == mDNSAddrType_IPv6) + else { - //LogSPS("NDP Announcement %d %s", rr->AnnounceCount, ARDisplayString(m,rr)); - //SendARP(m, 1, rr, rr->AddressProxy.ip.v4.b, zeroEthAddr.b, rr->AddressProxy.ip.v4.b, onesEthAddr.b); + LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); + for (r2 = rr; r2; r2=r2->next) + if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC)) + { + // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original + // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict. + if (r2->AddressProxy.type == mDNSAddrType_IPv6 && r2->AnnounceCount == WakeupCount) + { + LogSPS("NDP Announcement %2d Releasing traffic for H-MAC %.6a I-MAC %.6a %s", + r2->AnnounceCount-3, &r2->WakeUp.HMAC, &r2->WakeUp.IMAC, ARDisplayString(m,r2)); + SendNDP(m, NDP_Adv, NDP_Override, r2, &r2->AddressProxy.ip.v6, &r2->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth); + } + r2->LastAPTime = m->timenow; + if (--r2->AnnounceCount <= GoodbyeCount) r2->WakeUp.HMAC = zeroEthAddr; + } } } - else + else if (ResourceRecordIsValidAnswer(rr)) { - rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces - if (maxExistingAnnounceInterval < rr->ThisAPInterval) - maxExistingAnnounceInterval = rr->ThisAPInterval; - if (rr->UpdateBlocked) rr->UpdateBlocked = 0; + if (rr->AddressProxy.type) + { + rr->AnnounceCount--; + rr->ThisAPInterval *= 2; + rr->LastAPTime = m->timenow; + if (rr->AddressProxy.type == mDNSAddrType_IPv4) + { + LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", + rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr); + } + else if (rr->AddressProxy.type == mDNSAddrType_IPv6) + { + LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", + rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + } + } + else + { + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + if (maxExistingAnnounceInterval < rr->ThisAPInterval) + maxExistingAnnounceInterval = rr->ThisAPInterval; + if (rr->UpdateBlocked) rr->UpdateBlocked = 0; + } } } } @@ -2811,7 +1739,8 @@ mDNSlocal void SendResponses(mDNS *const m) if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) { rr->AnnounceCount--; - rr->ThisAPInterval *= 2; + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) + rr->ThisAPInterval *= 2; rr->LastAPTime = m->timenow; debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount); } @@ -2833,6 +1762,7 @@ mDNSlocal void SendResponses(mDNS *const m) while (intf) { + const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; int numDereg = 0; int numAnnounce = 0; int numAnswer = 0; @@ -2848,50 +1778,44 @@ mDNSlocal void SendResponses(mDNS *const m) { if (rr->SendRNow == intf->InterfaceID) { + RData *OldRData = rr->resrec.rdata; + mDNSu16 oldrdlength = rr->resrec.rdlength; + mDNSu8 active = (mDNSu8) + (rr->resrec.RecordType != kDNSRecordTypeDeregistering && + (m->SleepState != SleepState_Sleeping || intf->SPSAddr[0].type || intf->SPSAddr[1].type || intf->SPSAddr[2].type)); newptr = mDNSNULL; - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + if (rr->NewRData && active) { - newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); - if (newptr) { responseptr = newptr; numDereg++; } - } - else if (rr->NewRData && !m->SleepState) // If we have new data for this record - { - RData *OldRData = rr->resrec.rdata; - mDNSu16 oldrdlength = rr->resrec.rdlength; // See if we should send a courtesy "goodbye" for the old data before we replace it. - if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye) + if (ResourceRecordIsValidAnswer(rr) && rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) { - newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); if (newptr) { responseptr = newptr; numDereg++; rr->RequireGoodbye = mDNSfalse; } + else continue; // If this packet is already too full to hold the goodbye for this record, skip it for now and we'll retry later } - // Now try to see if we can fit the update in the same packet (not fatal if we can't) SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); - rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state - if (newptr) { responseptr = newptr; rr->RequireGoodbye = mDNStrue; } - SetNewRData(&rr->resrec, OldRData, oldrdlength); } - else + + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (newptr) { - mDNSu8 active = (m->SleepState != SleepState_Sleeping || intf->SPSAddr[0].type || intf->SPSAddr[1].type || intf->SPSAddr[2].type); - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); - rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state - if (newptr) - { - responseptr = newptr; - rr->RequireGoodbye = active; - if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; - } - - // The first time through (pktcount==0), if this record is verified unique - // (i.e. typically A, AAAA, SRV and TXT), set the flag to add an NSEC too. - if (!pktcount && active && rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->SendNSECNow) rr->SendNSECNow = (mDNSInterfaceID)1; + responseptr = newptr; + rr->RequireGoodbye = active; + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) numDereg++; + else if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; } + if (rr->NewRData && active) + SetNewRData(&rr->resrec, OldRData, oldrdlength); + + // The first time through (pktcount==0), if this record is verified unique + // (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too. + if (!pktcount && active && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow) + rr->SendNSECNow = mDNSInterfaceMark; + if (newptr) // If succeeded in sending, advance to next interface { // If sending on all interfaces, go to next interface; else we're finished now @@ -2928,12 +1852,13 @@ mDNSlocal void SendResponses(mDNS *const m) else if (newptr) // Else, try to add it if we can { // The first time through (pktcount==0), if this record is verified unique - // (i.e. typically A, AAAA, SRV and TXT), set the flag to add an NSEC too. - if (!pktcount && rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->SendNSECNow) rr->SendNSECNow = (mDNSInterfaceID)1; + // (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too. + if (!pktcount && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow) + rr->SendNSECNow = mDNSInterfaceMark; if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec); + newptr = PutRR_OS(newptr, &m->omsg.h.numAdditionals, &rr->resrec); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state if (newptr) { @@ -2951,8 +1876,11 @@ mDNSlocal void SendResponses(mDNS *const m) } // Third Pass. Add NSEC records, if there's space. + // When we're generating an NSEC record in response to a specify query for that type + // (recognized by rr->SendNSECNow == intf->InterfaceID) we should really put the NSEC in the Answer Section, + // not Additional Section, but for now it's easier to handle both cases in this Additional Section loop here. for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->SendNSECNow == (mDNSInterfaceID)1 || rr->SendNSECNow == intf->InterfaceID) + if (rr->SendNSECNow == mDNSInterfaceMark || rr->SendNSECNow == intf->InterfaceID) { AuthRecord nsec; mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); @@ -2968,25 +1896,45 @@ mDNSlocal void SendResponses(mDNS *const m) newptr = responseptr; if (!r2) // If we successfully built our NSEC record, add it to the packet now { - newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &nsec.resrec); + newptr = PutRR_OS(responseptr, &m->omsg.h.numAdditionals, &nsec.resrec); if (newptr) responseptr = newptr; } // If we successfully put the NSEC record, clear the SendNSECNow flag // If we consider this NSEC optional, then we unconditionally clear the SendNSECNow flag, even if we fail to put this additional record - if (newptr || rr->SendNSECNow == (mDNSInterfaceID)1) + if (newptr || rr->SendNSECNow == mDNSInterfaceMark) { rr->SendNSECNow = mDNSNULL; // Run through remainder of list clearing SendNSECNow flag for all other records which would generate the same NSEC for (r2 = rr->next; r2; r2=r2->next) if (SameResourceRecordNameClassInterface(r2, rr)) - if (r2->SendNSECNow == (mDNSInterfaceID)1 || r2->SendNSECNow == intf->InterfaceID) + if (r2->SendNSECNow == mDNSInterfaceMark || r2->SendNSECNow == intf->InterfaceID) r2->SendNSECNow = mDNSNULL; } } - if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals) + if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals) { + // If we have data to send, add OWNER option if necessary, then send packet + + if (OwnerRecordSpace) + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec); + if (newptr) { responseptr = newptr; LogSPS("SendResponses put %s", ARDisplayString(m, &opt)); } + else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1) + LogSPS("SendResponses: No space in packet for Owner OPT record (%d/%d/%d/%d) %s", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + else + LogMsg("SendResponses: How did we fail to have space for Owner OPT record (%d/%d/%d/%d) %s", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", numDereg, numDereg == 1 ? "" : "s", numAnnounce, numAnnounce == 1 ? "" : "s", @@ -3024,17 +1972,20 @@ mDNSlocal void SendResponses(mDNS *const m) if (rr->SendRNow) { - if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly) - LogMsg("SendResponses: No active interface to send: %02X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); + if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P) + LogMsg("SendResponses: No active interface %p to send: %p %02X %s", rr->SendRNow, rr->resrec.InterfaceID, rr->resrec.RecordType, ARDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } - if (rr->ImmedAnswer) + if (rr->ImmedAnswer || rr->resrec.RecordType == kDNSRecordTypeDeregistering) { if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - CompleteDeregistration(m, rr); // Don't touch rr after this + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->AnnounceCount == 0) + { + // For Unicast, when we get the response from the server, we will call CompleteDeregistration + if (!AuthRecord_uDNS(rr)) CompleteDeregistration(m, rr); // Don't touch rr after this + } else { rr->ImmedAnswer = mDNSNULL; @@ -3060,20 +2011,27 @@ mDNSlocal void SendResponses(mDNS *const m) // 5. For records with rroriginalttl set to zero, that means we really want to delete them immediately // (we have a new record with DelayDelivery set, waiting for the old record to go away before we can notify clients). #define CacheCheckGracePeriod(RR) ( \ - ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \ ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : \ ((RR)->resrec.rroriginalttl > 0 ) ? (mDNSPlatformOneSecond/10) : 0) -// Note: MUST call SetNextCacheCheckTime any time we change: +#define NextCacheCheckEvent(RR) ((RR)->NextRequiredQuery + CacheCheckGracePeriod(RR)) + +mDNSexport void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event) + { + if (m->rrcache_nextcheck[slot] - event > 0) + m->rrcache_nextcheck[slot] = event; + if (m->NextCacheCheck - event > 0) + m->NextCacheCheck = event; + } + +// Note: MUST call SetNextCacheCheckTimeForRecord any time we change: // rr->TimeRcvd // rr->resrec.rroriginalttl // rr->UnansweredQueries // rr->CRActiveQuestion -// Also, any time we set rr->DelayDelivery we should call SetNextCacheCheckTime to ensure m->NextCacheCheck is set if necessary -// Clearing rr->DelayDelivery does not require a call to SetNextCacheCheckTime -mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) +mDNSlocal void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const rr) { rr->NextRequiredQuery = RRExpireTime(rr); @@ -3083,17 +2041,11 @@ mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) { rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries); rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50); - verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks", - rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), - (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr)); + verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s", + (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr)); } - if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0) - m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)); - - if (rr->DelayDelivery) - if (m->NextCacheCheck - rr->DelayDelivery > 0) - m->NextCacheCheck = rr->DelayDelivery; + ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr)); } #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) @@ -3118,7 +2070,7 @@ mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, interval += m->RandomReconfirmDelay % ((interval/3) + 1); rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; rr->resrec.rroriginalttl = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond; - SetNextCacheCheckTime(m, rr); + SetNextCacheCheckTimeForRecord(m, rr); } debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s %p", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr), rr->CRActiveQuestion); @@ -3136,19 +2088,12 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; - mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); + mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); if (!newptr) { debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return(mDNSfalse); } - else if (newptr + *answerforecast >= limit) - { - verbosedebugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", - q->qname.c, DNSTypeName(q->qtype), newptr + *answerforecast - query->data); - query->h.numQuestions--; - return(mDNSfalse); - } else { mDNSu32 forecast = *answerforecast; @@ -3159,12 +2104,19 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) // If we have a resource record in our cache, if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + !(rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && // which is a shared (i.e. not unique) record type rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet SameNameRecordAnswersQuestion(&rr->resrec, q) && // which answers our question rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow > // and its half-way-to-expiry time is at least 1 second away mDNSPlatformOneSecond) // (also ensures we never include goodbye records with TTL=1) { + // We don't want to include unique records in the Known Answer section. The Known Answer section + // is intended to suppress floods of shared-record replies from many other devices on the network. + // That concept really does not apply to unique records, and indeed if we do send a query for + // which we have a unique record already in our cache, then including that unique record as a + // Known Answer, so as to suppress the only answer we were expecting to get, makes little sense. + *ka = rr; // Link this record into our known answer chain ka = &rr->NextInKAList; // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) @@ -3195,7 +2147,7 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer { rr->UnansweredQueries++; // indicate that we're expecting a response rr->LastUnansweredTime = m->timenow; - SetNextCacheCheckTime(m, rr); + SetNextCacheCheckTimeForRecord(m, rr); } return(mDNStrue); @@ -3377,235 +2329,232 @@ mDNSlocal void SendQueries(mDNS *const m) CacheRecord *KnownAnswerList = mDNSNULL; // 1. If time for a query, work out what we need to do - if (m->timenow - m->NextScheduledQuery >= 0) - { - CacheRecord *rr; - // We're expecting to send a query anyway, so see if any expiring cache records are close enough - // to their NextRequiredQuery to be worth batching them together with this one - FORALL_CACHERECORDS(slot, cg, rr) - if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) - if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0) - { - debugf("Sending %d%% cache expiration query for %s", 80 + 5 * rr->UnansweredQueries, CRDisplayString(m, rr)); - q = rr->CRActiveQuestion; - ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID); - // For uDNS queries (TargetQID non-zero) we adjust LastQTime, - // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly - if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If targeted query, mark it - else if (!mDNSOpaque16IsZero(q->TargetQID)) { q->LastQTime = m->timenow - q->ThisQInterval; rr->UnansweredQueries++; } - else if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID; - else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark; - } + // We're expecting to send a query anyway, so see if any expiring cache records are close enough + // to their NextRequiredQuery to be worth batching them together with this one + FORALL_CACHERECORDS(slot, cg, cr) + if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries) + if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0) + { + debugf("Sending %d%% cache expiration query for %s", 80 + 5 * cr->UnansweredQueries, CRDisplayString(m, cr)); + q = cr->CRActiveQuestion; + ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(cr)/20, cr->resrec.InterfaceID); + // For uDNS queries (TargetQID non-zero) we adjust LastQTime, + // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly + if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If targeted query, mark it + else if (!mDNSOpaque16IsZero(q->TargetQID)) { q->LastQTime = m->timenow - q->ThisQInterval; cr->UnansweredQueries++; } + else if (q->SendQNow == mDNSNULL) q->SendQNow = cr->resrec.InterfaceID; + else if (q->SendQNow != cr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark; + } - if (m->SuppressStdPort53Queries && m->timenow - m->SuppressStdPort53Queries >= 0) - m->SuppressStdPort53Queries = 0; // If suppression time has passed, clear it - - // Scan our list of questions to see which: - // *WideArea* queries need to be sent - // *unicast* queries need to be sent - // *multicast* queries we're definitely going to send - if (m->CurrentQuestion) - LogMsg("SendQueries ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); - m->CurrentQuestion = m->Questions; - while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + // Scan our list of questions to see which: + // *WideArea* queries need to be sent + // *unicast* queries need to be sent + // *multicast* queries we're definitely going to send + if (m->CurrentQuestion) + LogMsg("SendQueries ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + q = m->CurrentQuestion; + if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) { - q = m->CurrentQuestion; - if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID)) uDNS_CheckCurrentQuestion(m); - else if (mDNSOpaque16IsZero(q->TargetQID) && q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) - { - mDNSu8 *qptr = m->omsg.data; - const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); + mDNSu8 *qptr = m->omsg.data; + const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); - // If we fail to get a new on-demand socket (should only happen cases of the most extreme resource exhaustion), we'll try again next time - if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); - if (q->LocalSocket) - { - InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); - qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); - mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL); - q->ThisQInterval *= QuestionIntervalStep; - } - if (q->ThisQInterval > MaxQuestionInterval) - q->ThisQInterval = MaxQuestionInterval; - q->LastQTime = m->timenow; - q->LastQTxTime = m->timenow; - q->RecentAnswerPkts = 0; - q->SendQNow = mDNSNULL; - q->ExpectUnicastResp = NonZeroTime(m->timenow); - } - else if (mDNSOpaque16IsZero(q->TargetQID) && !q->Target.type && TimeToSendThisQuestion(q, m->timenow)) + // If we fail to get a new on-demand socket (should only happen cases of the most extreme resource exhaustion), we'll try again next time + if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (q->LocalSocket) { - //LogInfo("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - (q->LastQTime + q->ThisQInterval)); - q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces - if (maxExistingQuestionInterval < q->ThisQInterval) - maxExistingQuestionInterval = q->ThisQInterval; + InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); + qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); + mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL); + q->ThisQInterval *= QuestionIntervalStep; } - // If m->CurrentQuestion wasn't modified out from under us, advance it now - // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() depends on having - // m->CurrentQuestion point to the right question - if (q == m->CurrentQuestion) m->CurrentQuestion = m->CurrentQuestion->next; - } - m->CurrentQuestion = mDNSNULL; + if (q->ThisQInterval > MaxQuestionInterval) + q->ThisQInterval = MaxQuestionInterval; + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->SendQNow = mDNSNULL; + q->ExpectUnicastResp = NonZeroTime(m->timenow); + } + else if (mDNSOpaque16IsZero(q->TargetQID) && !q->Target.type && TimeToSendThisQuestion(q, m->timenow)) + { + //LogInfo("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + if (maxExistingQuestionInterval < q->ThisQInterval) + maxExistingQuestionInterval = q->ThisQInterval; + } + // If m->CurrentQuestion wasn't modified out from under us, advance it now + // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() depends on having + // m->CurrentQuestion point to the right question + if (q == m->CurrentQuestion) m->CurrentQuestion = m->CurrentQuestion->next; + } + while (m->CurrentQuestion) + { + LogInfo("SendQueries question loop 1: Skipping NewQuestion %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + m->CurrentQuestion = m->CurrentQuestion->next; + } + m->CurrentQuestion = mDNSNULL; - // Scan our list of questions - // (a) to see if there are any more that are worth accelerating, and - // (b) to update the state variables for *all* the questions we're going to send - // Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list, - // which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very - // next thing we do is scan the list and call SetNextQueryTime() for every question we find, so we know we end up with the right value. - m->NextScheduledQuery = m->timenow + 0x78000000; - for (q = m->Questions; q && q != m->NewQuestions; q=q->next) - { - if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow || - (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) + // Scan our list of questions + // (a) to see if there are any more that are worth accelerating, and + // (b) to update the state variables for *all* the questions we're going to send + // Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list, + // which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very + // next thing we do is scan the list and call SetNextQueryTime() for every question we find, so we know we end up with the right value. + m->NextScheduledQuery = m->timenow + 0x78000000; + for (q = m->Questions; q && q != m->NewQuestions; q=q->next) + { + if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow || + (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) + { + // If at least halfway to next query time, advance to next interval + // If less than halfway to next query time, then + // treat this as logically a repeat of the last transmission, without advancing the interval + if (m->timenow - (q->LastQTime + (q->ThisQInterval/2)) >= 0) { - // If at least halfway to next query time, advance to next interval - // If less than halfway to next query time, then - // treat this as logically a repeat of the last transmission, without advancing the interval - if (m->timenow - (q->LastQTime + q->ThisQInterval/2) >= 0) + //LogInfo("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d", + q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast); + q->ThisQInterval *= QuestionIntervalStep; + if (q->ThisQInterval > MaxQuestionInterval) + q->ThisQInterval = MaxQuestionInterval; + else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast && + !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash))) { - //LogInfo("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - (q->LastQTime + q->ThisQInterval)); - q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces - debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d", - q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast); - q->ThisQInterval *= QuestionIntervalStep; - if (q->ThisQInterval > MaxQuestionInterval) - q->ThisQInterval = MaxQuestionInterval; - else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast && - !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash))) - { - // Generally don't need to log this. - // It's not especially noteworthy if a query finds no results -- this usually happens for domain - // enumeration queries in the LL subdomain (e.g. "db._dns-sd._udp.0.0.254.169.in-addr.arpa") - // and when there simply happen to be no instances of the service the client is looking - // for (e.g. iTunes is set to look for RAOP devices, and the current network has none). - debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", - q->qname.c, DNSTypeName(q->qtype)); - // Sending third query, and no answers yet; time to begin doubting the source - ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); - } + // Generally don't need to log this. + // It's not especially noteworthy if a query finds no results -- this usually happens for domain + // enumeration queries in the LL subdomain (e.g. "db._dns-sd._udp.0.0.254.169.in-addr.arpa") + // and when there simply happen to be no instances of the service the client is looking + // for (e.g. iTunes is set to look for RAOP devices, and the current network has none). + debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", + q->qname.c, DNSTypeName(q->qtype)); + // Sending third query, and no answers yet; time to begin doubting the source + ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); } + } - // Mark for sending. (If no active interfaces, then don't even try.) - q->SendOnAll = (q->SendQNow == mDNSInterfaceMark); - if (q->SendOnAll) - { - q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID; - q->LastQTime = m->timenow; - } + // 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); + // If we recorded a duplicate suppression for this question less than half an interval ago, + // then we consider it recent enough that we don't need to do an identical query ourselves. + ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); - q->LastQTxTime = m->timenow; - q->RecentAnswerPkts = 0; - if (q->RequestUnicast) q->RequestUnicast--; - } - // For all questions (not just the ones we're sending) check what the next scheduled event will be - SetNextQueryTime(m,q); + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + if (q->RequestUnicast) q->RequestUnicast--; } + // For all questions (not just the ones we're sending) check what the next scheduled event will be + // We don't need to consider NewQuestions here because for those we'll set m->NextScheduledQuery in AnswerNewQuestion + SetNextQueryTime(m,q); } // 2. Scan our authoritative RR list to see what probes we might need to send - if (m->timenow - m->NextScheduledProbe >= 0) - { - m->NextScheduledProbe = m->timenow + 0x78000000; - if (m->CurrentRecord) - LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) + m->NextScheduledProbe = m->timenow + 0x78000000; + + if (m->CurrentRecord) + LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + ar = m->CurrentRecord; + m->CurrentRecord = ar->next; + if (!AuthRecord_uDNS(ar) && ar->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing... { - AuthRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (!AuthRecord_uDNS(rr) && 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 - (ar->LastAPTime + ar->ThisAPInterval) < 0) + { + SetNextAnnounceProbeTime(m, ar); + } + // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly + else if (ar->ProbeCount) { - // 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) + if (ar->AddressProxy.type == mDNSAddrType_IPv4) { - SetNextAnnounceProbeTime(m, rr); + LogSPS("SendQueries ARP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); + SendARP(m, 1, ar, &zerov4Addr, &zeroEthAddr, &ar->AddressProxy.ip.v4, &ar->WakeUp.IMAC); } - // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly - else if (rr->ProbeCount) + else if (ar->AddressProxy.type == mDNSAddrType_IPv6) { - if (rr->AddressProxy.type == mDNSAddrType_IPv4) - { - LogSPS("SendQueries ARP Probe %d %s %s", rr->ProbeCount, InterfaceNameForID(m, rr->resrec.InterfaceID), ARDisplayString(m,rr)); - SendARP(m, 1, rr, zerov4Addr.b, zeroEthAddr.b, rr->AddressProxy.ip.v4.b, rr->WakeUp.IMAC.b); - } - else if (rr->AddressProxy.type == mDNSAddrType_IPv6) - { - //LogSPS("SendQueries NDP Probe %d %s", rr->ProbeCount, ARDisplayString(m,rr)); - //SendARP(m, 1, rr, rr->AddressProxy.ip.v4.b, zeroEthAddr.b, rr->AddressProxy.ip.v4.b, onesEthAddr.b); - } - // Mark for sending. (If no active interfaces, then don't even try.) - rr->SendRNow = (!intf || rr->WakeUp.HMAC.l[0]) ? mDNSNULL : rr->resrec.InterfaceID ? rr->resrec.InterfaceID : intf->InterfaceID; - rr->LastAPTime = m->timenow; - // When we have a late conflict that resets a record to probing state we use a special marker value greater - // than DefaultProbeCountForTypeUnique. Here we detect that state and reset rr->ProbeCount back to the right value. - if (rr->ProbeCount > DefaultProbeCountForTypeUnique) - rr->ProbeCount = DefaultProbeCountForTypeUnique; - rr->ProbeCount--; - SetNextAnnounceProbeTime(m, rr); - if (rr->ProbeCount == 0) - { - // If this is the last probe for this record, then see if we have any matching records - // on our duplicate list which should similarly have their ProbeCount cleared to zero... - AuthRecord *r2; - for (r2 = m->DuplicateRecords; r2; r2=r2->next) - if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr)) - r2->ProbeCount = 0; - // ... then acknowledge this record to the client. - // We do this optimistically, just as we're about to send the third probe. - // This helps clients that both advertise and browse, and want to filter themselves - // from the browse results list, because it helps ensure that the registration - // confirmation will be delivered 1/4 second *before* the browse "add" event. - // A potential downside is that we could deliver a registration confirmation and then find out - // moments later that there's a name conflict, but applications have to be prepared to handle - // late conflicts anyway (e.g. on connection of network cable, etc.), so this is nothing new. - if (!rr->Acknowledged) AcknowledgeRecord(m, rr); - } + LogSPS("SendQueries NDP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); + // IPv6 source = zero + // No target hardware address + // IPv6 target address is address we're probing + // Ethernet destination address is Ethernet interface address of the Sleep Proxy client we're probing + SendNDP(m, NDP_Sol, 0, ar, &zerov6Addr, mDNSNULL, &ar->AddressProxy.ip.v6, &ar->WakeUp.IMAC); } - // else, if it has now finished probing, move it to state Verified, - // and update m->NextScheduledResponse so it will be announced - else + // Mark for sending. (If no active interfaces, then don't even try.) + ar->SendRNow = (!intf || ar->WakeUp.HMAC.l[0]) ? mDNSNULL : ar->resrec.InterfaceID ? ar->resrec.InterfaceID : intf->InterfaceID; + ar->LastAPTime = m->timenow; + // When we have a late conflict that resets a record to probing state we use a special marker value greater + // than DefaultProbeCountForTypeUnique. Here we detect that state and reset ar->ProbeCount back to the right value. + if (ar->ProbeCount > DefaultProbeCountForTypeUnique) + ar->ProbeCount = DefaultProbeCountForTypeUnique; + ar->ProbeCount--; + SetNextAnnounceProbeTime(m, ar); + if (ar->ProbeCount == 0) { - if (!rr->Acknowledged) AcknowledgeRecord(m, rr); // Defensive, just in case it got missed somehow - rr->resrec.RecordType = kDNSRecordTypeVerified; - rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; - rr->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; - SetNextAnnounceProbeTime(m, rr); + // If this is the last probe for this record, then see if we have any matching records + // on our duplicate list which should similarly have their ProbeCount cleared to zero... + AuthRecord *r2; + for (r2 = m->DuplicateRecords; r2; r2=r2->next) + if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, ar)) + r2->ProbeCount = 0; + // ... then acknowledge this record to the client. + // We do this optimistically, just as we're about to send the third probe. + // This helps clients that both advertise and browse, and want to filter themselves + // from the browse results list, because it helps ensure that the registration + // confirmation will be delivered 1/4 second *before* the browse "add" event. + // A potential downside is that we could deliver a registration confirmation and then find out + // moments later that there's a name conflict, but applications have to be prepared to handle + // late conflicts anyway (e.g. on connection of network cable, etc.), so this is nothing new. + if (!ar->Acknowledged) AcknowledgeRecord(m, ar); } } + // else, if it has now finished probing, move it to state Verified, + // and update m->NextScheduledResponse so it will be announced + else + { + if (!ar->Acknowledged) AcknowledgeRecord(m, ar); // Defensive, just in case it got missed somehow + ar->resrec.RecordType = kDNSRecordTypeVerified; + ar->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; + ar->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; + SetNextAnnounceProbeTime(m, ar); + } } - m->CurrentRecord = m->DuplicateRecords; - while (m->CurrentRecord) - { - AuthRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0 && !rr->Acknowledged) - AcknowledgeRecord(m, rr); - } + } + m->CurrentRecord = m->DuplicateRecords; + while (m->CurrentRecord) + { + ar = m->CurrentRecord; + m->CurrentRecord = ar->next; + if (ar->resrec.RecordType == kDNSRecordTypeUnique && ar->ProbeCount == 0 && !ar->Acknowledged) + AcknowledgeRecord(m, ar); } // 3. Now we know which queries and probes we're sending, // go through our interface list sending the appropriate queries on each interface while (intf) { - const int os = !intf->MAC.l[0] ? 0 : DNSOpt_Header_Space + (mDNSSameEthAddress(&m->PrimaryMAC, &intf->MAC) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space); - int OwnerRecordSpace = 0; - AuthRecord *rr; + const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; mDNSu8 *queryptr = m->omsg.data; - mDNSu8 *limit = m->omsg.data + AbsoluteMaxDNSMessageData; InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); if (!KnownAnswerList) { // Start a new known-answer list CacheRecord **kalistptr = &KnownAnswerList; - mDNSu32 answerforecast = 0; + mDNSu32 answerforecast = OwnerRecordSpace; // We start by assuming we'll need at least enough space to put the Owner Option // Put query questions in this packet for (q = m->Questions; q && q != m->NewQuestions; q=q->next) @@ -3619,55 +2568,44 @@ mDNSlocal void SendQueries(mDNS *const m) // If we're suppressing this question, or we successfully put it, update its SendQNow state if (SuppressOnThisInterface(q->DupSuppress, intf) || BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) - q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); - - // Once we've put at least one question, cut back our limit to the normal single-packet size - if (m->omsg.h.numQuestions) limit = m->omsg.data + NormalMaxDNSMessageData; + 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) + for (ar = m->ResourceRecords; ar; ar=ar->next) + if (ar->SendRNow == intf->InterfaceID) { - mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; + mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); - mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); + const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) - mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate; - if (newptr && newptr + forecast + os < limit) + mDNSu32 forecast = answerforecast + 12 + ar->resrec.rdestimate; + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, (mDNSu16)(ar->resrec.rrclass | ucbit)); + if (newptr) { - queryptr = newptr; - limit = m->omsg.data + NormalMaxDNSMessageData; - answerforecast = forecast; - OwnerRecordSpace = os; - rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); - rr->IncludeInProbe = mDNStrue; + queryptr = newptr; + answerforecast = forecast; + ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + ar->IncludeInProbe = mDNStrue; verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", - rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount); - } - else - { - verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - m->omsg.h.numQuestions--; + ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount); } } } - if (m->omsg.h.numQuestions) limit = m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace; - // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) while (KnownAnswerList) { CacheRecord *ka = KnownAnswerList; mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond; - mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, limit); + mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, + &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace); if (newptr) { verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", ka->resrec.name->c, DNSTypeName(ka->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); queryptr = newptr; - limit = m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace; KnownAnswerList = ka->NextInKAList; ka->NextInKAList = mDNSNULL; } @@ -3682,33 +2620,38 @@ mDNSlocal void SendQueries(mDNS *const m) } } - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->IncludeInProbe) + for (ar = m->ResourceRecords; ar; ar=ar->next) + if (ar->IncludeInProbe) { - mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec); - rr->IncludeInProbe = mDNSfalse; + mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &ar->resrec); + ar->IncludeInProbe = mDNSfalse; if (newptr) queryptr = newptr; - else LogMsg("SendQueries: How did we fail to have space for the Update record %s", ARDisplayString(m,rr)); + else LogMsg("SendQueries: How did we fail to have space for the Update record %s", ARDisplayString(m,ar)); } - if (OwnerRecordSpace) - { - AuthRecord opt; - mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); - opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record - opt.resrec.rdestimate = sizeof(rdataOPT); - SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); - LogSPS("SendQueries putting %s", ARDisplayString(m, &opt)); - queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, - &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); - if (!queryptr) - LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s", - m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); - } - if (queryptr > m->omsg.data) { + if (OwnerRecordSpace) + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + LogSPS("SendQueries putting %s", ARDisplayString(m, &opt)); + queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, + &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); + if (!queryptr) + LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + if (queryptr > m->omsg.data + NormalMaxDNSMessageData) + if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1) + LogMsg("SendQueries: Why did we generate oversized packet with OPT record %p %p %p (%d/%d/%d/%d) %s", + m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", @@ -3740,8 +2683,8 @@ mDNSlocal void SendQueries(mDNS *const m) for (ar = m->ResourceRecords; ar; ar=ar->next) if (ar->SendRNow) { - if (ar->resrec.InterfaceID != mDNSInterface_LocalOnly) - LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, ar)); + if (ar->resrec.InterfaceID != mDNSInterface_LocalOnly && ar->resrec.InterfaceID != mDNSInterface_P2P) + LogMsg("SendQueries: No active interface %p to send probe: %p %s", ar->SendRNow, ar->resrec.InterfaceID, ARDisplayString(m, ar)); ar->SendRNow = mDNSNULL; } @@ -3751,12 +2694,13 @@ mDNSlocal void SendQueries(mDNS *const m) // state machine ticking over we just pretend we did so. // If the interface does not come back in time, the cache record will expire naturally FORALL_CACHERECORDS(slot, cg, cr) - if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries && m->timenow - cr->NextRequiredQuery >= 0) - { - cr->UnansweredQueries++; - cr->CRActiveQuestion->SendQNow = mDNSNULL; - SetNextCacheCheckTime(m, cr); - } + if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries) + if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0) + { + cr->UnansweredQueries++; + cr->CRActiveQuestion->SendQNow = mDNSNULL; + SetNextCacheCheckTimeForRecord(m, cr); + } // 4c. Debugging check: Make sure we sent all our planned questions // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions @@ -3764,7 +2708,9 @@ mDNSlocal void SendQueries(mDNS *const m) for (q = m->Questions; q; q=q->next) if (q->SendQNow) { - LogMsg("SendQueries: No active interface to send: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + DNSQuestion *x; + for (x = m->NewQuestions; x; x=x->next) if (x == q) break; // Check if this question is a NewQuestion + LogMsg("SendQueries: No active interface %p to send %s question: %p %##s (%s)", q->SendQNow, x ? "new" : "old", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); q->SendQNow = mDNSNULL; } } @@ -3773,14 +2719,14 @@ mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAdd { int i, j; mDNSu8 *ptr = m->omsg.data; - - if (!InterfaceID) { LogMsg("SendWakeup: No InterfaceID specified"); return; } + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found", InterfaceID); return; } // 0x00 Destination address for (i=0; i<6; i++) *ptr++ = EthAddr->b[i]; - // 0x06 Source address (we just use zero -- BPF will fill in real interface address) - for (i=0; i<6; i++) *ptr++ = 0x0; + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0]; // 0x0C Ethertype (0x0842) *ptr++ = 0x08; @@ -3821,7 +2767,10 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco DNSQuestion *const q = m->CurrentQuestion; mDNSBool followcname = rr->resrec.RecordType != kDNSRecordTypePacketNegative && AddRecord && rr->resrec.rrtype == kDNSType_CNAME && q->qtype != kDNSType_CNAME; - verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s", q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr)); + verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s", + q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr)); + + if (QuerySuppressed(q)) return; // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue) // may be called twice, once when the record is received, and again when it's time to notify local clients. @@ -3831,9 +2780,10 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q) { if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count - debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion to %p for cache record %s", q, CRDisplayString(m,rr)); + debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion from %p to %p for cache record %s, CurrentAnswer %d", + rr->CRActiveQuestion, q, CRDisplayString(m,rr), q->CurrentAnswers); rr->CRActiveQuestion = q; // We know q is non-null - SetNextCacheCheckTime(m, rr); + SetNextCacheCheckTimeForRecord(m, rr); } // If this is: @@ -3868,7 +2818,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype)) { CacheRecord neg; - MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID); + MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID, q->qDNSServer); q->QuestionCallback(m, q, &neg.resrec, AddRecord); } else @@ -3878,27 +2828,135 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco // Note: Proceed with caution here because client callback function is allowed to do anything, // including starting/stopping queries, registering/deregistering records, etc. - if (followcname && m->CurrentQuestion == q && q->CNAMEReferrals < 10) + if (followcname && m->CurrentQuestion == q) { - const mDNSu32 c = q->CNAMEReferrals + 1; - // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect, - // and track CNAMEs coming and going, we should really create a subordinate query here, - // which we would subsequently cancel and retract if the CNAME referral record were removed. - // In reality this is such a corner case we'll ignore it until someone actually needs it. - LogInfo("AnswerCurrentQuestionWithResourceRecord: following CNAME referral for %s", CRDisplayString(m, rr)); - mDNS_StopQuery_internal(m, q); // Stop old query - AssignDomainName(&q->qname, &rr->resrec.rdata->u.name); // Update qname - q->qnamehash = DomainNameHashValue(&q->qname); // and namehash - mDNS_StartQuery_internal(m, q); // start new query - q->CNAMEReferrals = c; // and keep count of how many times we've done this + const mDNSBool selfref = SameDomainName(&q->qname, &rr->resrec.rdata->u.name); + if (q->CNAMEReferrals >= 10 || selfref) + LogMsg("AnswerCurrentQuestionWithResourceRecord: %p %##s (%s) NOT following CNAME referral %d%s for %s", + q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", CRDisplayString(m, rr)); + else + { + const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value + + // The SameDomainName check above is to ignore bogus CNAME records that point right back at + // themselves. Without that check we can get into a case where we have two duplicate questions, + // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals + // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because + // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates + // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals + // for either of them. This is not a problem for CNAME loops of two or more records because in + // those cases the newly re-appended question A has a different target name and therefore cannot be + // a duplicate of any other question ('B') which was itself a duplicate of the previous question A. + + // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect, + // and track CNAMEs coming and going, we should really create a subordinate query here, + // which we would subsequently cancel and retract if the CNAME referral record were removed. + // In reality this is such a corner case we'll ignore it until someone actually needs it. + LogInfo("AnswerCurrentQuestionWithResourceRecord: %p %##s (%s) following CNAME referral %d for %s", + q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, CRDisplayString(m, rr)); + + mDNS_StopQuery_internal(m, q); // Stop old query + AssignDomainName(&q->qname, &rr->resrec.rdata->u.name); // Update qname + q->qnamehash = DomainNameHashValue(&q->qname); // and namehash + // If a unicast query results in a CNAME that points to a .local, we need to re-try + // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal + // to try this as unicast query even though it is a .local name + if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname)) + { + LogInfo("AnswerCurrentQuestionWithResourceRecord: Resolving a .local CNAME %p %##s (%s) CacheRecord %s", + q, q->qname.c, DNSTypeName(q->qtype), CRDisplayString(m, rr)); + q->InterfaceID = mDNSInterface_Unicast; + } + mDNS_StartQuery_internal(m, q); // start new query + // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal, + // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero + q->CNAMEReferrals = c; + } } } +// New Questions are answered through AnswerNewQuestion. But there may not have been any +// matching cache records for the questions when it is called. There are two possibilities. +// +// 1) There are no cache records +// 2) There are cache records but the DNSServers between question and cache record don't match. +// +// In the case of (1), where there are no cache records and later we add them when we get a response, +// CacheRecordAdd/CacheRecordDeferredAdd will take care of adding the cache and delivering the ADD +// events to the application. If we already have a cache entry, then no ADD events are delivered +// unless the RDATA has changed +// +// In the case of (2) where we had the cache records and did not answer because of the DNSServer mismatch, +// we need to answer them whenever we change the DNSServer. But we can't do it at the instant the DNSServer +// changes because when we do the callback, the question can get deleted and the calling function would not +// know how to handle it. So, we run this function from mDNS_Execute to handle DNSServer changes on the +// question + +mDNSlocal void AnswerQuestionsForDNSServerChanges(mDNS *const m) + { + DNSQuestion *q; + DNSQuestion *qnext; + CacheRecord *rr; + mDNSu32 slot; + CacheGroup *cg; + + if (m->CurrentQuestion) + LogMsg("AnswerQuestionsForDNSServerChanges: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + for (q = m->Questions; q && q != m->NewQuestions; q = qnext) + { + qnext = q->next; + + // multicast or DNSServers did not change. + if (mDNSOpaque16IsZero(q->TargetQID)) continue; + if (!q->deliverAddEvents) continue; + + // We are going to look through the cache for this question since it changed + // its DNSserver last time. Reset it so that we don't call them again. Calling + // them again will deliver duplicate events to the application + q->deliverAddEvents = mDNSfalse; + if (QuerySuppressed(q)) continue; + m->CurrentQuestion = q; + slot = HashSlot(&q->qname); + cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + LogInfo("AnswerQuestionsForDNSServerChanges: Calling AnswerCurrentQuestionWithResourceRecord for question %p %##s using resource record %s", + q, q->qname.c, CRDisplayString(m, rr)); + // When this question penalizes a DNS server and has no more DNS servers to pick, we normally + // deliver a negative cache response and suspend the question for 60 seconds (see uDNS_CheckCurrentQuestion). + // But sometimes we may already find the negative cache entry and deliver that here as the process + // of changing DNS servers. When the cache entry is about to expire, we will resend the question and + // that time, we need to make sure that we have a valid DNS server. Otherwise, we will deliver + // a negative cache response without trying the server. + if (!q->qDNSServer && !q->DuplicateOf && rr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + DNSQuestion *qptr; + SetValidDNSServers(m, q); + q->qDNSServer = GetServerForQuestion(m, q); + for (qptr = q->next ; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } + } + q->CurrentAnswers++; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } + } + m->CurrentQuestion = mDNSNULL; + } + mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) { - rr->DelayDelivery = 0; // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared + rr->DelayDelivery = 0; if (m->CurrentQuestion) - LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); m->CurrentQuestion = m->Questions; while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) { @@ -3926,7 +2984,7 @@ mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *c else return(0); } -// CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. +// CacheRecordAdd is only called from CreateNewCacheEntry, *never* directly as a result of a client API call. // If new questions are created as a result of invoking client callbacks, they will be added to // the end of the question list, and m->NewQuestions will be set to indicate the first new question. // rr is a new CacheRecord just received into our cache @@ -3963,8 +3021,10 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) SetNextQueryTime(m,q); } } - verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", - rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); + verbosedebugf("CacheRecordAdd %p %##s (%s) %lu %#a:%d question %p", rr, rr->resrec.name->c, + DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl, rr->resrec.rDNSServer ? + &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ? + rr->resrec.rDNSServer->port : zeroIPPort), q); q->CurrentAnswers++; q->unansweredQueries = 0; if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; @@ -3997,7 +3057,7 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) m->CurrentQuestion = mDNSNULL; } - SetNextCacheCheckTime(m, rr); + SetNextCacheCheckTimeForRecord(m, rr); } // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. @@ -4041,7 +3101,8 @@ mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr) mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) { if (m->CurrentQuestion) - LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); m->CurrentQuestion = m->Questions; // We stop when we get to NewQuestions -- for new questions their CurrentAnswers/LargeAnswers/UniqueAnswers counters @@ -4049,14 +3110,31 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) { DNSQuestion *q = m->CurrentQuestion; - if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + // When a question enters suppressed state, we generate RMV events and generate a negative + // response. A cache may be present that answers this question e.g., cache entry generated + // before the question became suppressed. We need to skip the suppressed questions here as + // the RMV event has already been generated. + if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) { verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr)); q->FlappingInterface1 = mDNSNULL; q->FlappingInterface2 = mDNSNULL; + + // When a question changes DNS server, it is marked with deliverAddEvents if we find any + // cache entry corresponding to the new DNS server. Before we deliver the ADD event, the + // cache entry may be removed in which case CurrentAnswers can be zero. + if (q->deliverAddEvents && !q->CurrentAnswers) + { + LogInfo("CacheRecordRmv: Question %p %##s (%s) deliverAddEvents set, DNSServer %#a:%d", + q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, + mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort)); + m->CurrentQuestion = q->next; + continue; + } if (q->CurrentAnswers == 0) - LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", - q, q->qname.c, DNSTypeName(q->qtype)); + LogMsg("CacheRecordRmv ERROR!!: How can CurrentAnswers already be zero for %p %##s (%s) DNSServer %#a:%d", + q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, + mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort)); else { q->CurrentAnswers--; @@ -4116,7 +3194,7 @@ mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering // CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all // callbacks for old records are delivered before callbacks for newer records. -mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *const cg) +mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGroup *const cg) { CacheRecord **rp = &cg->members; @@ -4130,10 +3208,24 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *const cg) if (m->timenow - event >= 0) // If expired, delete it { *rp = rr->next; // Cut it from the list - verbosedebugf("CheckCacheExpiration: Deleting%7d %4d %p %s", + verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s", m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away { + DNSQuestion *q = rr->CRActiveQuestion; + // When a cache record is about to expire, we expect to do four queries at 80-82%, 85-87%, 90-92% and + // then 95-97% of the TTL. If the DNS server does not respond, then we will remove the cache entry + // before we pick a new DNS server. As the question interval is set to MaxQuestionInterval, we may + // not send out a query anytime soon. Hence, we need to reset the question interval. If this is + // a normal deferred ADD case, then AnswerCurrentQuestionWithResourceRecord will reset it to + // MaxQuestionInterval. If we have inactive questions referring to negative cache entries, + // don't ressurect them as they will deliver duplicate "No such Record" ADD events + if (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived && ActiveQuestion(q)) + { + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + SetNextQueryTime(m, q); + } CacheRecordRmv(m, rr); m->rrcache_active--; } @@ -4141,6 +3233,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *const cg) } else // else, not expired; see if we need to query { + // If waiting to delay delivery, do nothing until then if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) event = rr->DelayDelivery; else @@ -4149,13 +3242,13 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *const cg) 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 + event = NextCacheCheckEvent(rr); // then just record when we want the next query else // else trigger our question to go out now { // Set NextScheduledQuery to timenow so that SendQueries() will run. // SendQueries() will see that we have records close to expiration, and send FEQs for them. m->NextScheduledQuery = m->timenow; - // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), + // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTimeForRecord(), // which will correctly update m->NextCacheCheck for us. event = m->timenow + 0x3FFFFFFF; } @@ -4163,8 +3256,8 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *const cg) } verbosedebugf("CheckCacheExpiration:%6d %5d %s", (event - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr)); - if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0) - m->NextCacheCheck = (event + CacheCheckGracePeriod(rr)); + if (m->rrcache_nextcheck[slot] - event > 0) + m->rrcache_nextcheck[slot] = event; rp = &rr->next; } } @@ -4173,18 +3266,48 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, CacheGroup *const cg) m->lock_rrcache = 0; } +// Caller should hold the lock +mDNSlocal void AnswerSuppressUnusableQuestion(mDNS *const m, DNSQuestion *q) + { + LogInfo("AnswerSuppressUnusableQuestion: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (!m->CurrentQuestion) LogMsg("AnswerSuppressUnusableQuestion: ERROR!! CurrentQuestion not set"); + + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL); + AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache); + if (m->CurrentQuestion == q) q->ThisQInterval = 0; // Deactivate this question + // Don't touch the question after this + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } + mDNSlocal void AnswerNewQuestion(mDNS *const m) { mDNSBool ShouldQueryImmediately = mDNStrue; - DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer + DNSQuestion *const q = m->NewQuestions; // Grab the question we're going to answer const mDNSu32 slot = HashSlot(&q->qname); CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (cg) CheckCacheExpiration(m, cg); - m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration(); - + if (cg) CheckCacheExpiration(m, slot, cg); + if (m->NewQuestions != q) { LogInfo("AnswerNewQuestion: Question deleted while doing CheckCacheExpiration"); goto exit; } + m->NewQuestions = q->next; + // Advance NewQuestions to the next *after* calling CheckCacheExpiration, because if we advance it first + // then CheckCacheExpiration may give this question add/remove callbacks, and it's not yet ready for that. + // + // Also, CheckCacheExpiration() calls CacheRecordDeferredAdd() and CacheRecordRmv(), which invoke + // client callbacks, which may delete their own or any other question. Our mechanism for detecting + // whether our current m->NewQuestions question got deleted by one of these callbacks is to store the + // value of m->NewQuestions in 'q' before calling CheckCacheExpiration(), and then verify afterwards + // that they're still the same. If m->NewQuestions has changed (because mDNS_StopQuery_internal + // advanced it), that means the question was deleted, so we no longer need to worry about answering + // it (and indeed 'q' is now a dangling pointer, so dereferencing it at all would be bad, and the + // values we computed for slot and cg are now stale and relate to a question that no longer exists). + // + // We can't use the usual m->CurrentQuestion mechanism for this because CacheRecordDeferredAdd() and + // CacheRecordRmv() both use that themselves when walking the list of (non-new) questions generating callbacks. + // Fortunately mDNS_StopQuery_internal auto-advances both m->CurrentQuestion *AND* m->NewQuestions when + // deleting a question, so luckily we have an easy alternative way of detecting if our question got deleted. + if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!"); // This should be safe, because calling the client's question callback may cause the // question list to be modified, but should not ever cause the rrcache list to be modified. @@ -4192,21 +3315,23 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) // be advanced, and we'll exit out of the loop m->lock_rrcache = 1; if (m->CurrentQuestion) - LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted if (q->NoAnswer == NoAnswer_Fail) { LogMsg("AnswerNewQuestion: NoAnswer_Fail %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any); + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->qDNSServer); q->NoAnswer = NoAnswer_Normal; // Temporarily turn off answer suppression AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache); q->NoAnswer = NoAnswer_Fail; // Restore NoAnswer state m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } + if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); goto exit; } // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records - if (m->CurrentQuestion == q && q->InterfaceID == mDNSInterface_Any) + if (q->InterfaceID == mDNSInterface_Any) { if (m->CurrentRecord) LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); @@ -4215,7 +3340,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) { AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) { AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, mDNStrue); @@ -4224,10 +3349,12 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) } m->CurrentRecord = mDNSNULL; } + if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while while giving LocalOnly record answers"); goto exit; } - if (m->CurrentQuestion != q) debugf("AnswerNewQuestion: question deleted while giving LocalOnly record answers"); - - if (m->CurrentQuestion == q) + // If we are not supposed to answer this question, generate a negative response. + // Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question + if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; AnswerSuppressUnusableQuestion(m, q); q->SuppressQuery = mDNStrue; } + else { CacheRecord *rr; for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) @@ -4255,10 +3382,11 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) ShouldQueryImmediately = mDNSfalse; } + // We don't use LogInfo for this "Question deleted" message because it happens so routinely that + // it's not remotely remarkable, and therefore unlikely to be of much help tracking down bugs. + if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving cache answers"); goto exit; } - if (m->CurrentQuestion != q) debugf("AnswerNewQuestion: question deleted while giving cache answers"); - - if (m->CurrentQuestion == q && ShouldQueryImmediately && ActiveQuestion(q)) + if (ShouldQueryImmediately && ActiveQuestion(q)) { debugf("AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); q->ThisQInterval = InitialQuestionInterval; @@ -4270,11 +3398,16 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) m->RandomQueryDelay = (mDNSPlatformOneSecond + mDNSRandom(mDNSPlatformOneSecond*5) - 1) / 50 + 1; q->LastQTime += m->RandomQueryDelay; } - - if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0) - m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval); } + // IN ALL CASES make sure that m->NextScheduledQuery is set appropriately. + // In cases where m->NewQuestions->DelayAnswering is set, we may have delayed generating our + // answers for this question until *after* its scheduled transmission time, in which case + // m->NextScheduledQuery may now be set to 'never', and in that case -- even though we're *not* doing + // ShouldQueryImmediately -- we still need to make sure we set m->NextScheduledQuery correctly. + SetNextQueryTime(m,q); + +exit: m->CurrentQuestion = mDNSNULL; m->lock_rrcache = 0; } @@ -4289,7 +3422,8 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); if (m->CurrentQuestion) - LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted if (m->CurrentRecord) @@ -4452,7 +3586,7 @@ mDNSexport void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60; rr->UnansweredQueries = MaxUnansweredQueries; rr->resrec.rroriginalttl = 0; - SetNextCacheCheckTime(m, rr); + SetNextCacheCheckTimeForRecord(m, rr); } mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) @@ -4485,24 +3619,26 @@ mDNSlocal void CheckProxyRecords(mDNS *const m, AuthRecord *list) while (m->CurrentRecord) { AuthRecord *rr = m->CurrentRecord; - if (rr->WakeUp.HMAC.l[0]) + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering && rr->WakeUp.HMAC.l[0]) { - if (m->timenow - rr->TimeExpire < 0) // If proxy record not expired yet, update m->NextScheduledSPS + // If m->SPSSocket is NULL that means we're not acting as a sleep proxy any more, + // so we need to cease proxying for *all* records we may have, expired or not. + if (m->SPSSocket && m->timenow - rr->TimeExpire < 0) // If proxy record not expired yet, update m->NextScheduledSPS { if (m->NextScheduledSPS - rr->TimeExpire > 0) m->NextScheduledSPS = rr->TimeExpire; } - else // else proxy record expired, so remove it + else // else proxy record expired, so remove it { - LogSPS("mDNS_Execute: Removing %d H-MAC %.6a I-MAC %.6a %d %s", + LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s", m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr)); SetSPSProxyListChanged(rr->resrec.InterfaceID); mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this -- memory may have been free'd } } - // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, - // because the list may have been changed in that call. + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now m->CurrentRecord = rr->next; } @@ -4515,10 +3651,16 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) if (m->timenow - m->NextScheduledEvent >= 0) { int i; + AuthRecord *head, *tail; verbosedebugf("mDNS_Execute"); + if (m->CurrentQuestion) - LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + if (m->CurrentRecord) + LogMsg("mDNS_Execute: ERROR m->CurrentRecord already set: %s", ARDisplayString(m, m->CurrentRecord)); // 1. If we're past the probe suppression time, we can clear it if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0; @@ -4529,18 +3671,29 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) // 3. Purge our cache of stale old records if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0) { - mDNSu32 slot; + mDNSu32 slot, numchecked = 0; m->NextCacheCheck = m->timenow + 0x3FFFFFFF; for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) { - CacheGroup **cp = &m->rrcache_hash[slot]; - while (*cp) + if (m->timenow - m->rrcache_nextcheck[slot] >= 0) { - CheckCacheExpiration(m, *cp); - if ((*cp)->members) cp=&(*cp)->next; - else ReleaseCacheGroup(m, cp); + CacheGroup **cp = &m->rrcache_hash[slot]; + m->rrcache_nextcheck[slot] = m->timenow + 0x3FFFFFFF; + while (*cp) + { + debugf("m->NextCacheCheck %4d Slot %3d %##s", numchecked, slot, *cp ? (*cp)->name : (domainname*)"\x04NULL"); + numchecked++; + CheckCacheExpiration(m, slot, *cp); + if ((*cp)->members) cp=&(*cp)->next; + else ReleaseCacheGroup(m, cp); + } } + // Even if we didn't need to actually check this slot yet, still need to + // factor its nextcheck time into our overall NextCacheCheck value + if (m->NextCacheCheck - m->rrcache_nextcheck[slot] > 0) + m->NextCacheCheck = m->rrcache_nextcheck[slot]; } + debugf("m->NextCacheCheck %4d checked, next in %d", numchecked, m->NextCacheCheck - m->timenow); } if (m->timenow - m->NextScheduledSPS >= 0) @@ -4552,6 +3705,9 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) SetSPSProxyListChanged(mDNSNULL); // Perform any deferred BPF reconfiguration now + // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) + if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) m->AnnounceOwner = 0; + if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) { m->DelaySleep = 0; @@ -4569,19 +3725,89 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) AnswerNewQuestion(m); } if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); + + // Make sure we deliver *all* local RMV events, and clear the corresponding rr->AnsweredLocalQ flags, *before* + // we begin generating *any* new ADD events in the m->NewLocalOnlyQuestions and m->NewLocalRecords loops below. + for (i=0; i<1000 && m->LocalRemoveEvents; i++) + { + m->LocalRemoveEvents = mDNSfalse; + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + debugf("mDNS_Execute: Generating local RMV events for %s", ARDisplayString(m, rr)); + rr->resrec.RecordType = kDNSRecordTypeShared; + AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); + if (m->CurrentRecord == rr) // If rr still exists in list, restore its state now + { + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + rr->AnsweredLocalQ = mDNSfalse; + } + } + if (m->CurrentRecord == rr) // If m->CurrentRecord was not auto-advanced, do it ourselves now + m->CurrentRecord = rr->next; + } + } + if (i >= 1000) LogMsg("mDNS_Execute: m->LocalRemoveEvents exceeded loop limit"); for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); - for (i=0; i<1000 && m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords); i++) + head = tail = mDNSNULL; + for (i=0; i<1000 && m->NewLocalRecords && m->NewLocalRecords != head; i++) { AuthRecord *rr = m->NewLocalRecords; m->NewLocalRecords = m->NewLocalRecords->next; - AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue); + if (LocalRecordReady(rr)) + { + debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr)); + AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue); + } + else if (!rr->next) + { + // If we have just one record that is not ready, we don't have to unlink and + // reinsert. As the NewLocalRecords will be NULL for this case, the loop will + // terminate and set the NewLocalRecords to rr. + debugf("mDNS_Execute: Just one LocalAuthRecord %s, breaking out of the loop early", ARDisplayString(m, rr)); + if (head != mDNSNULL || m->NewLocalRecords != mDNSNULL) + LogMsg("mDNS_Execute: ERROR!!: head %p, NewLocalRecords %p", head, m->NewLocalRecords); + + head = rr; + } + else + { + AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records + debugf("mDNS_Execute: Skipping LocalAuthRecord %s", ARDisplayString(m, rr)); + // if this is the first record we are skipping, move to the end of the list. + // if we have already skipped records before, append it at the end. + while (*p && *p != rr) p=&(*p)->next; + if (*p) *p = rr->next; // Cut this record from the list + else { LogMsg("mDNS_Execute: ERROR!! Cannot find record %s in ResourceRecords list", ARDisplayString(m, rr)); break; } + if (!head) + { + while (*p) p=&(*p)->next; + *p = rr; + head = tail = rr; + } + else + { + tail->next = rr; + tail = rr; + } + rr->next = mDNSNULL; + } } - if (i >= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit"); + m->NewLocalRecords = head; + debugf("mDNS_Execute: Setting NewLocalRecords to %s", (head ? ARDisplayString(m, head) : "NULL")); - // 5. See what packets we need to send + if (i >= 1000) LogMsg("mDNS_Execute: m->NewLocalRecords exceeded loop limit"); + + // 5. Some questions may have picked a new DNS server and the cache may answer these questions now. + AnswerQuestionsForDNSServerChanges(m); + + // 6. See what packets we need to send if (m->mDNSPlatformStatus != mStatus_NoError || (m->SleepState == SleepState_Sleeping)) DiscardDeregistrations(m); if (m->mDNSPlatformStatus == mStatus_NoError && (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0)) @@ -4594,7 +3820,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) // 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 + // 7. Send Query packets. This may cause some probing records to advance to announcing state if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m); if (m->timenow - m->NextScheduledQuery >= 0) { @@ -4603,7 +3829,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) m->timenow, m->NextScheduledQuery, m->timenow - m->NextScheduledQuery); m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond; for (q = m->Questions; q && q != m->NewQuestions; q=q->next) - if (ActiveQuestion(q) && q->LastQTime + q->ThisQInterval - m->timenow <= 0) + if (ActiveQuestion(q) && m->timenow - NextQSendTime(q) >= 0) LogMsg("mDNS_Execute: SendQueries didn't send %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); } if (m->timenow - m->NextScheduledProbe >= 0) @@ -4613,7 +3839,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond; } - // 7. Send Response packets, including probing records just advanced to announcing state + // 8. Send Response packets, including probing records just advanced to announcing state if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m); if (m->timenow - m->NextScheduledResponse >= 0) { @@ -4625,6 +3851,12 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) // Clear RandomDelay values, ready to pick a new different value next time m->RandomQueryDelay = 0; m->RandomReconfirmDelay = 0; + +#ifndef UNICAST_DISABLED + if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) UpdateAllSRVRecords(m); + if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m); + if (m->timenow - m->NextuDNSEvent >= 0) uDNS_Tasks(m); +#endif } // Note about multi-threaded systems: @@ -4647,9 +3879,6 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale // by the time it gets to the timer callback function). -#ifndef UNICAST_DISABLED - uDNS_Execute(m); -#endif mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value return(m->NextScheduledEvent); } @@ -4677,8 +3906,11 @@ mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question, // Otherwise we can get the situation where the A query completes really fast (with an NXDOMAIN result) and the // caller then gives up waiting for the AAAA result while we're still in the process of setting up the tunnel. // To level the playing field, we block both A and AAAA queries while tunnel setup is in progress, and then - // returns results for both at the same time. - if (RRTypeIsAddressType(question->qtype) && question->AuthInfo && question->AuthInfo->AutoTunnel && question->QuestionCallback != AutoTunnelCallback) + // returns results for both at the same time. If we are looking for the _autotunnel6 record, then skip this logic + // as this would trigger looking up _autotunnel6._autotunnel6 and end up failing the original query. + + if (RRTypeIsAddressType(question->qtype) && PrivateQuery(question) && + !SameDomainLabel(question->qname.c, (const mDNSu8 *)"\x0c_autotunnel6")&& question->QuestionCallback != AutoTunnelCallback) { question->NoAnswer = NoAnswer_Suspended; AddNewClientTunnel(m, question); @@ -4689,7 +3921,8 @@ mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question, if (!question->DuplicateOf) { debugf("ActivateUnicastQuery: %##s %s%s%s", - question->qname.c, DNSTypeName(question->qtype), question->AuthInfo ? " (Private)" : "", ScheduleImmediately ? " ScheduleImmediately" : ""); + question->qname.c, DNSTypeName(question->qtype), PrivateQuery(question) ? " (Private)" : "", ScheduleImmediately ? " ScheduleImmediately" : ""); + question->CNAMEReferrals = 0; if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } if (question->LongLived) { @@ -4714,13 +3947,14 @@ mDNSexport void mDNSCoreRestartQueries(mDNS *const m) #ifndef UNICAST_DISABLED // Retrigger all our uDNS questions if (m->CurrentQuestion) - LogMsg("mDNSCoreRestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + LogMsg("mDNSCoreRestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); m->CurrentQuestion = m->Questions; while (m->CurrentQuestion) { q = m->CurrentQuestion; m->CurrentQuestion = m->CurrentQuestion->next; - if (!mDNSOpaque16IsZero(q->TargetQID)) ActivateUnicastQuery(m, q, mDNStrue); + if (!mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) ActivateUnicastQuery(m, q, mDNStrue); } #endif @@ -4743,10 +3977,58 @@ mDNSexport void mDNSCoreRestartQueries(mDNS *const m) #pragma mark - Power Management (Sleep/Wake) #endif +mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) + { +#ifndef IDLESLEEPCONTROL_DISABLED + mDNSBool allowSleep = mDNStrue; + + if (m->SystemSleepOnlyIfWakeOnLAN) + { + // Don't sleep if we are a proxy for any services + if (m->ProxyRecords) + { + allowSleep = mDNSfalse; + LogInfo("Sleep disabled because we are proxying %d records", m->ProxyRecords); + } + + if (allowSleep && mDNSCoreHaveAdvertisedMulticastServices(m)) + { + // Scan the list of active interfaces + NetworkInterfaceInfo *intf; + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + if (intf->McastTxRx) + { + // Disallow sleep if this interface doesn't support NetWake + if (!intf->NetWake) + { + allowSleep = mDNSfalse; + LogInfo("Sleep disabled because %s does not support NetWake", intf->ifname); + break; + } + + // Disallow sleep if there is no sleep proxy server + if (FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL) == mDNSNULL) + { + allowSleep = mDNSfalse; + LogInfo("Sleep disabled because %s has no sleep proxy", intf->ifname); + break; + } + } + } + } +#endif /* !defined(IDLESLEEPCONTROL_DISABLED) */ + } + +#if 0 + // Call the platform code to enable/disable sleep + mDNSPlatformSetAllowSleep(m, allowSleep); +#endif + } + mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, const mDNSOpaque16 id) { - const int ownerspace = mDNSSameEthAddress(&m->PrimaryMAC, &intf->MAC) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space; - const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + ownerspace; + const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC); const int sps = intf->NextSPSAttempt / 3; AuthRecord *rr; @@ -4818,6 +4100,9 @@ mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, co else { mStatus err; + // Once we've attempted to register, we need to include our OWNER option in our packets when we re-awaken + m->SentSleepProxyRegistration = mDNStrue; + LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss @@ -4870,18 +4155,19 @@ mDNSlocal void RetrySPSRegistrations(mDNS *const m) mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) { NetworkInterfaceInfo *intf = (NetworkInterfaceInfo *)question->QuestionContext; - int sps = question - intf->NetWakeResolve; + int sps = (int)(question - intf->NetWakeResolve); (void)m; // Unused LogSPS("NetWakeResolve: SPS: %d Add: %d %s", sps, AddRecord, RRDisplayString(m, answer)); if (!AddRecord) return; // Don't care about REMOVE events if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs - mDNS_StopQuery(m, question); - question->ThisQInterval = -1; + // if (answer->rrtype == kDNSType_AAAA && sps == 0) return; // To test failing to resolve sleep proxy's address if (answer->rrtype == kDNSType_SRV) { + // 1. Got the SRV record; now look up the target host's IPv6 link-local address + mDNS_StopQuery(m, question); intf->SPSPort[sps] = answer->rdata->u.srv.port; AssignDomainName(&question->qname, &answer->rdata->u.srv.target); question->qtype = kDNSType_AAAA; @@ -4889,20 +4175,28 @@ mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const Resour } else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == sizeof(mDNSv6Addr) && mDNSv6AddressIsLinkLocal(&answer->rdata->u.ipv6)) { + // 2. Got the target host's IPv6 link-local address; record address and initiate an SPS registration if appropriate + mDNS_StopQuery(m, question); + question->ThisQInterval = -1; intf->SPSAddr[sps].type = mDNSAddrType_IPv6; intf->SPSAddr[sps].ip.v6 = answer->rdata->u.ipv6; mDNS_Lock(m); if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID); // If we're ready for this result, use it now mDNS_Unlock(m); } - else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == 0) // If negative answer for IPv6, look for IPv4 addresses instead + else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == 0) { + // 3. Got negative response -- target host apparently has IPv6 disabled -- so try looking up the target host's IPv4 address(es) instead + mDNS_StopQuery(m, question); LogSPS("NetWakeResolve: SPS %d %##s has no IPv6 address, will try IPv4 instead", sps, question->qname.c); question->qtype = kDNSType_A; mDNS_StartQuery(m, question); } else if (answer->rrtype == kDNSType_A && answer->rdlength == sizeof(mDNSv4Addr)) { + // 4. Got an IPv4 address for the target host; record address and initiate an SPS registration if appropriate + mDNS_StopQuery(m, question); + question->ThisQInterval = -1; intf->SPSAddr[sps].type = mDNSAddrType_IPv4; intf->SPSAddr[sps].ip.v4 = answer->rdata->u.ipv4; mDNS_Lock(m); @@ -4920,9 +4214,26 @@ mDNSexport mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m) return mDNSfalse; } +mDNSlocal void SendSleepGoodbyes(mDNS *const m) + { + AuthRecord *rr; + m->SleepState = SleepState_Sleeping; + +#ifndef UNICAST_DISABLED + SleepRecordRegistrations(m); // If we have no SPS, need to deregister our uDNS records +#endif /* UNICAST_DISABLED */ + + // Mark all the records we need to deregister and send them + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + rr->ImmedAnswer = mDNSInterfaceMark; + SendResponses(m); + } + // BeginSleepProcessing is called, with the lock held, from either mDNS_Execute or mDNSCoreMachineSleep mDNSlocal void BeginSleepProcessing(mDNS *const m) { + mDNSBool SendGoodbyes = mDNStrue; const CacheRecord *sps[3] = { mDNSNULL }; m->NextScheduledSPRetry = m->timenow; @@ -4935,13 +4246,25 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) { if (!intf->NetWake) LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname); +#if APPLE_OSX_mDNSResponder + else if (ActivateLocalProxy(m, intf->ifname) == mStatus_NoError) + { + SendGoodbyes = mDNSfalse; + LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname); + // This will leave m->SleepState set to SleepState_Transferring, + // which is okay because with no outstanding resolves, or updates in flight, + // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed + } +#endif // APPLE_OSX_mDNSResponder else { FindSPSInCache(m, &intf->NetWakeBrowse, sps); - if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found %d", intf->ifname, &intf->ip, intf->NetWakeBrowse.ThisQInterval); + if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)", + intf->ifname, &intf->ip, NextQSendTime(&intf->NetWakeBrowse) - m->timenow, intf->NetWakeBrowse.ThisQInterval); else { int i; + SendGoodbyes = mDNSfalse; intf->NextSPSAttempt = 0; intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond; // Don't need to set m->NextScheduledSPRetry here because we already set "m->NextScheduledSPRetry = m->timenow" above @@ -4969,22 +4292,10 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) } } - if (!sps[0]) // If we didn't find even one Sleep Proxy + if (SendGoodbyes) // If we didn't find even one Sleep Proxy { - AuthRecord *rr; LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server"); - m->SleepState = SleepState_Sleeping; - -#ifndef UNICAST_DISABLED - SleepServiceRegistrations(m); - SleepRecordRegistrations(m); // If we have no SPS, need to deregister our uDNS records -#endif - - // Mark all the records we need to deregister and send them - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) - rr->ImmedAnswer = mDNSInterfaceMark; - SendResponses(m); + SendSleepGoodbyes(m); } } @@ -4995,12 +4306,11 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { AuthRecord *rr; - mDNS_Lock(m); - LogSPS("%s (old state %d) at %ld", sleep ? "Sleeping" : "Waking", m->SleepState, m->timenow); if (sleep && !m->SleepState) // Going to sleep { + mDNS_Lock(m); // If we're going to sleep, need to stop advertising that we're a Sleep Proxy Server if (m->SPSSocket) { @@ -5016,20 +4326,25 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { // If we just woke up moments ago, allow ten seconds for networking to stabilize before going back to sleep LogSPS("mDNSCoreMachineSleep: Re-sleeping immediately after waking; will delay for %d ticks", m->DelaySleep - m->timenow); - m->SleepLimit = m->DelaySleep + mDNSPlatformOneSecond * 10; + m->SleepLimit = NonZeroTime(m->DelaySleep + mDNSPlatformOneSecond * 10); } else { m->DelaySleep = 0; - m->SleepLimit = m->timenow + mDNSPlatformOneSecond * 10; + m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10); BeginSleepProcessing(m); } #ifndef UNICAST_DISABLED SuspendLLQs(m); #endif + mDNS_Unlock(m); + // RemoveAutoTunnel6Record needs to be called outside the lock, as it grabs the lock also. +#if APPLE_OSX_mDNSResponder + RemoveAutoTunnel6Record(m); +#endif LogSPS("mDNSCoreMachineSleep: m->SleepState %d (%s) seq %d", m->SleepState, - m->SleepState == SleepState_Transferring ? "Transferring" : + m->SleepState == SleepState_Transferring ? "Transferring" : m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", m->SleepSeqNum); } else if (!sleep) // Waking up @@ -5039,11 +4354,20 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) CacheRecord *cr; NetworkInterfaceInfo *intf; + mDNS_Lock(m); + // Reset SleepLimit back to 0 now that we're awake again. + m->SleepLimit = 0; + // If we were previously sleeping, but now we're not, increment m->SleepSeqNum to indicate that we're entering a new period of wakefulness if (m->SleepState != SleepState_Awake) { m->SleepState = SleepState_Awake; m->SleepSeqNum++; + if (m->SentSleepProxyRegistration) // Include OWNER option in packets for 60 seconds after waking + { + m->SentSleepProxyRegistration = mDNSfalse; + m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); + } // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) // then we enforce a minimum delay of 16 seconds before we begin sleep processing. // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., @@ -5053,10 +4377,8 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) if (m->SPSState == 3) { - mDNS_DropLockBeforeCallback(); // mDNS_DeregisterService expects to be called without the lock held, so we emulate that here m->SPSState = 0; - mDNSCoreBeSleepProxyServer(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower); - mDNS_ReclaimLockAfterCallback(); + mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower); } // In case we gave up waiting and went to sleep before we got an ack from the Sleep Proxy, @@ -5071,10 +4393,9 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) // and reactivtate service registrations m->NextSRVUpdate = NonZeroTime(m->timenow + mDNSPlatformOneSecond); - LogInfo("WakeServiceRegistrations %d %d", m->timenow, m->NextSRVUpdate); + LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); // 2. Re-validate our cache records - m->NextCacheCheck = m->timenow; FORALL_CACHERECORDS(slot, cg, cr) mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake); @@ -5089,87 +4410,131 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); rr->AnnounceCount = InitialAnnounceCount; + rr->SendNSECNow = mDNSNULL; InitializeLastAPTime(m, rr); } // 4. Refresh NAT mappings // We don't want to have to assume that all hardware can necessarily keep accurate // track of passage of time while asleep, so on wake we refresh our NAT mappings + // We typically wake up with no interfaces active, so there's no need to rush to try to find our external address. + // When we get a network configuration change, mDNSMacOSXNetworkChanged calls uDNS_SetupDNSConfig, which calls + // mDNS_SetPrimaryInterfaceInfo, which then sets m->retryGetAddr to immediately request our external address from the NAT gateway. m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - m->retryGetAddr = m->timenow; + m->retryGetAddr = m->timenow + mDNSPlatformOneSecond * 5; + LogInfo("mDNSCoreMachineSleep: retryGetAddr in %d %d", m->retryGetAddr - m->timenow, m->timenow); RecreateNATMappings(m); + mDNS_Unlock(m); } - - mDNS_Unlock(m); } -mDNSexport mDNSBool mDNSCoreReadyForSleep(mDNS *m) +mDNSexport mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now) { DNSQuestion *q; AuthRecord *rr; - ServiceRecordSet *srs; NetworkInterfaceInfo *intf; mDNS_Lock(m); - if (m->NextScheduledSPRetry - m->timenow > 0) goto notready; + if (m->DelaySleep) goto notready; - m->NextScheduledSPRetry = m->timenow + 0x40000000UL; + // If we've not hit the sleep limit time, and it's not time for our next retry, we can skip these checks + if (m->SleepLimit - now > 0 && m->NextScheduledSPRetry - now > 0) goto notready; - if (m->DelaySleep) goto notready; + m->NextScheduledSPRetry = now + 0x40000000UL; // See if we might need to retransmit any lost Sleep Proxy Registrations for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) if (intf->NextSPSAttempt >= 0) { - if (m->timenow - intf->NextSPSAttemptTime >= 0) + if (now - intf->NextSPSAttemptTime >= 0) { - LogSPS("ReadyForSleep retrying SPS %s %d", intf->ifname, intf->NextSPSAttempt); + LogSPS("mDNSCoreReadyForSleep: retrying for %s SPS %d try %d", + intf->ifname, intf->NextSPSAttempt/3, intf->NextSPSAttempt); SendSPSRegistration(m, intf, zeroID); + // Don't need to "goto notready" here, because if we do still have record registrations + // that have not been acknowledged yet, we'll catch that in the record list scan below. } else if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0) m->NextScheduledSPRetry = intf->NextSPSAttemptTime; } - // Scan list of private LLQs, and make sure they've all completed their handshake with the server - for (q = m->Questions; q; q = q->next) - if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->ReqLease == 0 && q->tcp) + // Scan list of interfaces, and see if we're still waiting for any sleep proxy resolves to complete + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + int sps = (intf->NextSPSAttempt == 0) ? 0 : (intf->NextSPSAttempt-1)/3; + if (intf->NetWakeResolve[sps].ThisQInterval >= 0) { - LogSPS("ReadyForSleep waiting for LLQ %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - goto notready; + LogSPS("mDNSCoreReadyForSleep: waiting for SPS Resolve %s %##s (%s)", + intf->ifname, intf->NetWakeResolve[sps].qname.c, DNSTypeName(intf->NetWakeResolve[sps].qtype)); + goto spsnotready; } + } - // Scan list of interfaces - for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) - if (intf->NetWakeResolve[0].ThisQInterval >= 0) + // Scan list of registered records + for (rr = m->ResourceRecords; rr; rr = rr->next) + if (!AuthRecord_uDNS(rr)) + if (!mDNSOpaque16IsZero(rr->updateid)) + { LogSPS("mDNSCoreReadyForSleep: waiting for SPS Update ID %d %s", mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto spsnotready; } + + // Scan list of private LLQs, and make sure they've all completed their handshake with the server + for (q = m->Questions; q; q = q->next) + if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->ReqLease == 0 && q->tcp) { - LogSPS("ReadyForSleep waiting for SPS Resolve %s %##s (%s)", intf->ifname, intf->NetWakeResolve[0].qname.c, DNSTypeName(intf->NetWakeResolve[0].qtype)); + LogSPS("mDNSCoreReadyForSleep: waiting for LLQ %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); goto notready; } // Scan list of registered records for (rr = m->ResourceRecords; rr; rr = rr->next) - { if (AuthRecord_uDNS(rr)) { if (rr->state == regState_Refresh && rr->tcp) - { LogSPS("ReadyForSleep waiting for Record Update ID %d %s", mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; } - } - else - { - if (!mDNSOpaque16IsZero(rr->updateid)) - { LogSPS("ReadyForSleep waiting for SPS Update ID %d %s", mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; } + { LogSPS("mDNSCoreReadyForSleep: waiting for Record Update ID %d %s", mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; } + #if APPLE_OSX_mDNSResponder + if (!RecordReadyForSleep(m, rr)) { LogSPS("mDNSCoreReadyForSleep: waiting for %s", ARDisplayString(m, rr)); goto notready; } + #endif } - } - - // Scan list of registered services - for (srs = m->ServiceRegistrations; srs; srs = srs->uDNS_next) - if (srs->state == regState_NoTarget && srs->tcp) goto notready; mDNS_Unlock(m); return mDNStrue; +spsnotready: + + // If we failed to complete sleep proxy registration within ten seconds, we give up on that + // and allow up to ten seconds more to complete wide-area deregistration instead + if (now - m->SleepLimit >= 0) + { + LogMsg("Failed to register with SPS, now sending goodbyes"); + + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + if (intf->NetWakeBrowse.ThisQInterval >= 0) + { + LogSPS("ReadyForSleep mDNS_DeactivateNetWake %s %##s (%s)", + intf->ifname, intf->NetWakeResolve[0].qname.c, DNSTypeName(intf->NetWakeResolve[0].qtype)); + mDNS_DeactivateNetWake_internal(m, intf); + } + + for (rr = m->ResourceRecords; rr; rr = rr->next) + if (!AuthRecord_uDNS(rr)) + if (!mDNSOpaque16IsZero(rr->updateid)) + { + LogSPS("ReadyForSleep clearing updateid for %s", ARDisplayString(m, rr)); + rr->updateid = zeroID; + } + + // We'd really like to allow up to ten seconds more here, + // but if we don't respond to the sleep notification within 30 seconds + // we'll be put back to sleep forcibly without the chance to schedule the next maintenance wake. + // Right now we wait 16 sec after wake for all the interfaces to come up, then we wait up to 10 seconds + // more for SPS resolves and record registrations to complete, which puts us at 26 seconds. + // If we allow just one more second to send our goodbyes, that puts us at 27 seconds. + m->SleepLimit = now + mDNSPlatformOneSecond * 1; + + SendSleepGoodbyes(m); + } + notready: mDNS_Unlock(m); return mDNSfalse; @@ -5243,7 +4608,7 @@ mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const m // *** if (LegacyQuery) { - maxttl = 10; + maxttl = kStaticCacheTTL; for (i=0; i<query->h.numQuestions; i++) // For each question... { DNSQuestion q; @@ -5415,7 +4780,7 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q { ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); if (!ptr) break; - if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) { FoundUpdate = mDNStrue; if (PacketRRConflict(m, our, &m->rec.r)) @@ -5426,11 +4791,11 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q if (result) { const char *const msg = (result < 0) ? "lost:" : (result > 0) ? "won: " : "tie: "; - LogMsg("ResolveSimultaneousProbe: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); - LogMsg("ResolveSimultaneousProbe: Our Record %d %s %08lX %s", our->ProbeCount, msg, our->resrec.rdatahash, ARDisplayString(m, our)); + LogMsg("ResolveSimultaneousProbe: %p Pkt Record: %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogMsg("ResolveSimultaneousProbe: %p Our Record %d %s %08lX %s", our->resrec.InterfaceID, our->ProbeCount, msg, our->resrec.rdatahash, ARDisplayString(m, our)); } // If we lost the tie-break for simultaneous probes, we don't immediately give up, because we might be seeing stale packets on the network. - // Instead we pause for one second, to give the other host (if real) a change to establish its name, and then try probing again. + // Instead we pause for one second, to give the other host (if real) a chance to establish its name, and then try probing again. // If there really is another live host out there with the same name, it will answer our probes and we'll then rename. if (result < 0) { @@ -5444,8 +4809,8 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q #if 0 else { - LogMsg("ResolveSimultaneousProbe: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); - LogMsg("ResolveSimultaneousProbe: Our Record ign: %08lX %s", our->resrec.rdatahash, ARDisplayString(m, our)); + LogMsg("ResolveSimultaneousProbe: %p Pkt Record: %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r)); + LogMsg("ResolveSimultaneousProbe: %p Our Record %d ign: %08lX %s", our->resrec.InterfaceID, our->ProbeCount, our->resrec.rdatahash, ARDisplayString(m, our)); } #endif } @@ -5462,14 +4827,47 @@ mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const Res mDNSu32 slot = HashSlot(pktrr->name); CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr); CacheRecord *rr; + mDNSBool match; for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalSameNameRecord(pktrr, &rr->resrec)) break; + { + match = !pktrr->InterfaceID ? pktrr->rDNSServer == rr->resrec.rDNSServer : pktrr->InterfaceID == rr->resrec.InterfaceID; + if (match && IdenticalSameNameRecord(pktrr, &rr->resrec)) break; + } return(rr); } +// Called from mDNSCoreReceiveUpdate when we get a sleep proxy registration request, +// to check our lists and discard any stale duplicates of this record we already have +mDNSlocal void ClearIdenticalProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist) + { + if (m->CurrentRecord) + LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = thelist; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) + if (IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec)) + { + LogSPS("ClearIdenticalProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s", + m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } + } + // Called from ProcessQuery when we get an mDNS packet with an owner record in it mDNSlocal void ClearProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist) { + if (m->CurrentRecord) + LogMsg("ClearProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); m->CurrentRecord = thelist; while (m->CurrentRecord) { @@ -5477,13 +4875,28 @@ mDNSlocal void ClearProxyRecords(mDNS *const m, const OwnerOptData *const owner, if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) if (owner->seq != rr->WakeUp.seq || m->timenow - rr->TimeRcvd > mDNSPlatformOneSecond * 60) { - LogSPS("ClearProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s", - m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + if (rr->AddressProxy.type == mDNSAddrType_IPv6) + { + // We don't do this here because we know that the host is waking up at this point, so we don't send + // Unsolicited Neighbor Advertisements -- even Neighbor Advertisements agreeing with what the host should be + // saying itself -- because it can cause some IPv6 stacks to falsely conclude that there's an address conflict. + #if MDNS_USE_Unsolicited_Neighbor_Advertisements + LogSPS("NDP Announcement -- Releasing traffic for H-MAC %.6a I-MAC %.6a %s", + &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, &rr->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth); + #endif + } + LogSPS("ClearProxyRecords: Removing %3d AC %2d %02X H-MAC %.6a I-MAC %.6a %d %d %s", + m->ProxyRecords, rr->AnnounceCount, rr->resrec.RecordType, + &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) rr->resrec.RecordType = kDNSRecordTypeShared; + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it, since real host is now back and functional mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); } - // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, - // because the list may have been changed in that call. + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now m->CurrentRecord = rr->next; } @@ -5494,7 +4907,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, DNSMessage *const response) { - mDNSBool FromLocalSubnet = srcaddr && AddressIsLocalSubnet(m, InterfaceID, srcaddr); + mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); AuthRecord *ResponseRecords = mDNSNULL; AuthRecord **nrp = &ResponseRecords; CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated @@ -5515,7 +4928,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con if (ptr) { ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &m->rec); - if (m->rec.r.resrec.rrtype == kDNSType_OPT) + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) { const rdataOPT *opt; const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; @@ -5600,10 +5013,10 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; } } - else if (rr->resrec.RecordType == kDNSRecordTypeVerified) + else if ((rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && ResourceRecordIsValidAnswer(rr)) { // If we don't have any answers for this question, but we do own another record with the same name, - // then mark it to generate an NSEC record on this interface + // then we'll want to mark it to generate an NSEC record on this interface if (!NSECAnswer) NSECAnswer = rr; } } @@ -5698,70 +5111,72 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con CacheRecord *ourcacherr; ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); if (!ptr) goto exit; - - // See if this Known-Answer suppresses any of our currently planned answers - for (rr=ResponseRecords; rr; rr=rr->NextResponse) - if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) - { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } - - // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) - for (rr=m->ResourceRecords; rr; rr=rr->next) + if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) { - // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression - if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr)) + // See if this Known-Answer suppresses any of our currently planned answers + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) + { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } + + // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) + for (rr=m->ResourceRecords; rr; rr=rr->next) { - if (srcaddr->type == mDNSAddrType_IPv4) - { - if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; - } - else if (srcaddr->type == mDNSAddrType_IPv6) - { - if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; - } - if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) + // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression + if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr)) { - rr->ImmedAnswer = mDNSNULL; - rr->ImmedUnicast = mDNSfalse; -#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); -#endif + if (srcaddr->type == mDNSAddrType_IPv4) + { + if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; + } + else if (srcaddr->type == mDNSAddrType_IPv6) + { + if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; + } + if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) + { + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; + #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); + #endif + } } } - } - - ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); - -#if ENABLE_MULTI_PACKET_QUERY_SNOOPING - // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, - // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list). - if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) - { - ourcacherr->MPUnansweredKA++; - ourcacherr->MPExpectingKA = mDNSfalse; - } -#endif - - // Having built our ExpectedAnswers list from the questions in this packet, we then remove - // any records that are suppressed by the Known Answer list in this packet. - eap = &ExpectedAnswers; - while (*eap) - { - CacheRecord *cr = *eap; - if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec)) - { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; } - else eap = &cr->NextInKAList; - } - - // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. - if (!ourcacherr) - { - dqp = &DupQuestions; - while (*dqp) + + ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); + + #if ENABLE_MULTI_PACKET_QUERY_SNOOPING + // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, + // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list). + if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) + { + ourcacherr->MPUnansweredKA++; + ourcacherr->MPExpectingKA = mDNSfalse; + } + #endif + + // Having built our ExpectedAnswers list from the questions in this packet, we then remove + // any records that are suppressed by the Known Answer list in this packet. + eap = &ExpectedAnswers; + while (*eap) + { + CacheRecord *cr = *eap; + if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec)) + { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; } + else eap = &cr->NextInKAList; + } + + // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. + if (!ourcacherr) { - DNSQuestion *q = *dqp; - if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) - { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } - else dqp = &q->NextInDQList; + dqp = &DupQuestions; + while (*dqp) + { + DNSQuestion *q = *dqp; + if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) + { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } + else dqp = &q->NextInDQList; + } } } m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it @@ -5912,7 +5327,7 @@ exit: debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); #endif - SetNextCacheCheckTime(m, cr); + SetNextCacheCheckTimeForRecord(m, cr); } // If we've seen multiple unanswered queries for this record, @@ -5980,27 +5395,27 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, { mDNSu8 *responseend = mDNSNULL; mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr && - !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr); + !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr)) { LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)", + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes (Multicast, but no InterfaceID)", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, - msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", - msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.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"); + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); return; } verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, - msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", - msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.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"); + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, !mDNSSameIPPort(srcport, MulticastDNSPort), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg); @@ -6033,50 +5448,70 @@ struct UDPSocket_struct mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port }; -mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, const mDNSIPPort port, const mDNSOpaque16 id, const DNSQuestion *const question) +mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, const mDNSIPPort port, const mDNSOpaque16 id, const DNSQuestion *const question, mDNSBool tcp) { DNSQuestion *q; for (q = m->Questions; q; q=q->next) - if (q->LocalSocket && - mDNSSameIPPort (q->LocalSocket->port, port) && + { + if (!tcp && !q->LocalSocket) continue; + if (mDNSSameIPPort(tcp ? q->tcpSrcPort : q->LocalSocket->port, port) && mDNSSameOpaque16(q->TargetQID, id) && q->qtype == question->qtype && q->qclass == question->qclass && q->qnamehash == question->qnamehash && SameDomainName(&q->qname, &question->qname)) return(q); + } return(mDNSNULL); } -mDNSlocal mDNSBool ExpectingUnicastResponseForRecord(mDNS *const m, const mDNSAddr *const srcaddr, const mDNSBool SrcLocal, const mDNSIPPort port, const mDNSOpaque16 id, const CacheRecord *const rr) +mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, + const mDNSAddr *const srcaddr, const mDNSBool SrcLocal, const mDNSIPPort port, const mDNSOpaque16 id, const CacheRecord *const rr, mDNSBool tcp) { DNSQuestion *q; (void)id; (void)srcaddr; + + // Unicast records have zero as InterfaceID + if (rr->resrec.InterfaceID) return mDNSNULL; + for (q = m->Questions; q; q=q->next) - if (!q->DuplicateOf && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + if (!q->DuplicateOf && UnicastResourceRecordAnswersQuestion(&rr->resrec, q)) { if (!mDNSOpaque16IsZero(q->TargetQID)) { debugf("ExpectingUnicastResponseForRecord msg->h.id %d q->TargetQID %d for %s", mDNSVal16(id), mDNSVal16(q->TargetQID), CRDisplayString(m, rr)); + if (mDNSSameOpaque16(q->TargetQID, id)) { - if (q->LocalSocket && mDNSSameIPPort(q->LocalSocket->port, port)) return(mDNStrue); + mDNSIPPort srcp; + if (!tcp) + { + srcp = q->LocalSocket ? q->LocalSocket->port : zeroIPPort; + } + else + { + srcp = q->tcpSrcPort; + } + if (mDNSSameIPPort(srcp, port)) return(q); + // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking // if (TrustedSource(m, srcaddr)) return(mDNStrue); LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) [q->Target %#a:%d] from %#a:%d %s", - q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(q->LocalSocket ? q->LocalSocket->port : zeroIPPort), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); - return(mDNSfalse); + q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(srcp), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); + return(mDNSNULL); } } else { if (SrcLocal && q->ExpectUnicastResp && (mDNSu32)(m->timenow - q->ExpectUnicastResp) < (mDNSu32)(mDNSPlatformOneSecond*2)) - return(mDNStrue); + return(q); } } - return(mDNSfalse); + } + return(mDNSNULL); } // Certain data types need more space for in-memory storage than their in-packet rdlength would imply @@ -6095,7 +5530,7 @@ mDNSlocal mDNSu16 GetRDLengthMem(const ResourceRecord *const rr) } } -mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg) +mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay) { CacheRecord *rr = mDNSNULL; mDNSu16 RDLength = GetRDLengthMem(&m->rec.r.resrec); @@ -6112,8 +5547,9 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C { RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer *rr = m->rec.r; // Block copy the CacheRecord object - rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment - rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header + rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment + rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header + rr->DelayDelivery = delay; // If this is an oversized record with external storage allocated, copy rdata to external storage if (rr->resrec.rdata == (RData*)&rr->smallrdatastorage && RDLength > InlineCacheRDSize) @@ -6126,15 +5562,8 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C rr->next = mDNSNULL; // Clear 'next' pointer *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list cg->rrcache_tail = &(rr->next); // Advance tail pointer - if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) - rr->DelayDelivery = NonZeroTime(m->timenow); - else if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask && // If marked unique, - rr->resrec.rdata->MaxRDLength != 0) // and non-negative, assume we may have - rr->DelayDelivery = NonZeroTime(m->timenow + mDNSPlatformOneSecond); // to delay delivery of this 'add' event - else - rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot); - CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us + CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTimeForRecord(m, rr); for us } return(rr); } @@ -6149,7 +5578,7 @@ mDNSlocal void RefreshCacheRecord(mDNS *const m, CacheRecord *rr, mDNSu32 ttl) rr->MPUnansweredKA = 0; rr->MPExpectingKA = mDNSfalse; #endif - SetNextCacheCheckTime(m, rr); + SetNextCacheCheckTimeForRecord(m, rr); } mDNSexport void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease) @@ -6177,8 +5606,8 @@ mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // T else // else not LLQ (standard uDNS response) { // The TTL is already capped to a maximum value in GetLargeResourceRecord, but just to be extra safe we - // also do this check here to make sure we can't get integer overflow below - if (ttl > 0x8000000UL) ttl = 0x8000000UL; + // also do this check here to make sure we can't get overflow below when we add a quarter to the TTL + if (ttl > 0x60000000UL / mDNSPlatformOneSecond) ttl = 0x60000000UL / mDNSPlatformOneSecond; // Adjustment factor to avoid race condition: // Suppose real record as TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100. @@ -6193,7 +5622,14 @@ mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // T // For mDNS, TTL zero means "delete this record" // For uDNS, TTL zero means: this data is true at this moment, but don't cache it. // For the sake of network efficiency, we impose a minimum effective TTL of 15 seconds. - // If we allow a TTL of less than 2 seconds things really break (e.g. we end up making a negative cache entry). + // This means that we'll do our 80, 85, 90, 95% queries at 12.00, 12.75, 13.50, 14.25 seconds + // respectively, and then if we get no response, delete the record from the cache at 15 seconds. + // This gives the server up to three seconds to respond between when we send our 80% query at 12 seconds + // and when we delete the record at 15 seconds. Allowing cache lifetimes less than 15 seconds would + // (with the current code) result in the server having even less than three seconds to respond + // before we deleted the record and reported a "remove" event to any active questions. + // Furthermore, with the current code, if we were to allow a TTL of less than 2 seconds + // then things really break (e.g. we end up making a negative cache entry). // In the future we may want to revisit this and consider properly supporting non-cached (TTL=0) uDNS answers. if (ttl < 15) ttl = 15; } @@ -6214,8 +5650,9 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { int i; mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); - mDNSBool ResponseSrcLocal = !srcaddr || AddressIsLocalSubnet(m, InterfaceID, srcaddr); - uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport); + mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + DNSQuestion *llqMatch = mDNSNULL; + uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch); // "(CacheRecord*)1" is a special (non-zero) end-of-list marker // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList @@ -6230,14 +5667,15 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, int firstadditional = firstauthority + response->h.numAuthorities; int totalrecords = firstadditional + response->h.numAdditionals; const mDNSu8 *ptr = response->data; + DNSServer *uDNSServer = mDNSNULL; debugf("Received Response from %#-15a addressed to %#-15a on %p with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s LLQType %d", + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes LLQType %d", srcaddr, dstaddr, InterfaceID, - response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,", - response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", + response->h.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", LLQType); + response->h.numAdditionals, response->h.numAdditionals == 1 ? " " : "s", end - response->data, LLQType); // According to RFC 2181 <http://www.ietf.org/rfc/rfc2181.txt> // When a DNS client receives a reply with TC @@ -6281,7 +5719,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { DNSQuestion q, *qptr = mDNSNULL; ptr = getQuestion(response, ptr, end, InterfaceID, &q); - if (ptr && (!dstaddr || (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q)))) + if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr))) { if (!failure) { @@ -6289,7 +5727,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, const mDNSu32 slot = HashSlot(&q.qname); CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (q.InterfaceID == rr->resrec.InterfaceID && SameNameRecordAnswersQuestion(&rr->resrec, &q)) + if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) { debugf("uDNS marking %p %##s (%s) %p %s", q.InterfaceID, q.qname.c, DNSTypeName(q.qtype), rr->resrec.InterfaceID, CRDisplayString(m, rr)); @@ -6302,8 +5740,8 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { if (qptr) { - LogInfo("Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); - PushDNSServerToEnd(m, qptr); + LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); + PenalizeDNSServer(m, qptr); } returnEarly = mDNStrue; } @@ -6334,11 +5772,34 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, (i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd; ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec); if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting + if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) { m->rec.r.resrec.RecordType = 0; continue; } // Don't want to cache OPT or TSIG pseudo-RRs - if (m->rec.r.resrec.rrtype == kDNSType_OPT || m->rec.r.resrec.rrtype == kDNSType_TSIG) - { m->rec.r.resrec.RecordType = 0; continue; } - + if (m->rec.r.resrec.rrtype == kDNSType_TSIG) { m->rec.r.resrec.RecordType = 0; continue; } + if (m->rec.r.resrec.rrtype == kDNSType_OPT) + { + const rdataOPT *opt; + const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; + // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently + // delete all our own AuthRecords (which are identified by having zero MAC tags on them). + for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++) + if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0]) + { + ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords); + ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords); + } + m->rec.r.resrec.RecordType = 0; + continue; + } + + // if a CNAME record points to itself, then don't add it to the cache + if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name)) + { + LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c); + m->rec.r.resrec.RecordType = 0; + continue; + } + // When we receive uDNS LLQ responses, we assume a long cache lifetime -- // In the case of active LLQs, we'll get remove events when the records actually do go away // In the case of polling LLQs, we assume the record remains valid until the next poll @@ -6347,7 +5808,57 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // If response was not sent via LL multicast, // then see if it answers a recent query of ours, which would also make it acceptable for caching. - if (!AcceptableResponse) AcceptableResponse = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r); + if (!ResponseMCast) + { + if (LLQType) + { + // For Long Lived queries that are both sent over UDP and Private TCP, LLQType is set. + // Even though it is AcceptableResponse, we need a matching DNSServer pointer for the + // queries to get ADD/RMV events. To lookup the question, we can't use + // ExpectingUnicastResponseForRecord as the port numbers don't match. uDNS_recvLLQRespose + // has already matched the question using the 64 bit Id in the packet and we use that here. + + if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer; + } + else if (!AcceptableResponse || !dstaddr) + { + // For responses that come over TCP (Responses that can't fit within UDP) or TLS (Private queries + // that are not long lived e.g., AAAA lookup in a Private domain), it is indicated by !dstaddr. + // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that + // we create. + + DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); + + // Intialize the DNS server on the resource record which will now filter what questions we answer with + // this record. + // + // We could potentially lookup the DNS server based on the source address, but that may not work always + // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came + // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based + // on the "id" and "source port", then this response answers the question and assume the response + // came from the same DNS server that we sent the query to. + + if (q != mDNSNULL) + { + AcceptableResponse = mDNStrue; + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; + } + } + else + { + // If we can't find a matching question, we need to see whether we have seen records earlier that matched + // the question. The code below does that. So, make this record unacceptable for now + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); + AcceptableResponse = mDNSfalse; + } + } + } + } // 1. Check that this packet resource record does not conflict with any of ours if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype != kDNSType_NSEC) @@ -6451,15 +5962,26 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, for (cr = CacheFlushRecords; cr != (CacheRecord*)1; cr = cr->NextInCFList) { domainname *target = GetRRDomainNameTarget(&cr->resrec); + // When we issue a query for A record, the response might contain both a CNAME and A records. Only the CNAME would + // match the question and we already created a cache entry in the previous pass of this loop. Now when we process + // the A record, it does not match the question because the record name here is the CNAME. Hence we try to + // match with the previous records to make it an AcceptableResponse. We have to be careful about setting the + // DNSServer value that we got in the previous pass. This can happen for other record types like SRV also. + if (target && cr->resrec.rdatahash == m->rec.r.resrec.namehash && SameDomainName(target, m->rec.r.resrec.name)) - { AcceptableResponse = mDNStrue; break; } + { + debugf("mDNSCoreReceiveResponse: Found a matching entry for %##s in the CacheFlushRecords", m->rec.r.resrec.name->c); + AcceptableResponse = mDNStrue; + m->rec.r.resrec.rDNSServer = uDNSServer; + break; + } } } // 2. See if we want to add this packet resource record to our cache // We only try to cache answers if we have a cache to put them in // Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query - if (!AcceptableResponse) debugf("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r)); + if (!AcceptableResponse) LogInfo("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r)); if (m->rrcache_size && AcceptableResponse) { const mDNSu32 slot = HashSlot(m->rec.r.resrec.name); @@ -6469,13 +5991,13 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // 2a. Check if this packet resource record is already in our cache for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) { + mDNSBool match = !InterfaceID ? m->rec.r.resrec.rDNSServer == rr->resrec.rDNSServer : rr->resrec.InterfaceID == InterfaceID; // If we found this exact resource record, refresh its TTL - if (rr->resrec.InterfaceID == InterfaceID && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) + if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) { if (m->rec.r.resrec.rdlength > InlineCacheRDSize) verbosedebugf("Found record size %5d interface %p already in cache: %s", m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); - rr->TimeRcvd = m->timenow; if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) { @@ -6492,20 +6014,54 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, } } - if (!mDNSPlatformMemSame(m->rec.r.resrec.rdata->u.data, rr->resrec.rdata->u.data, m->rec.r.resrec.rdlength)) + if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS)) { // If the rdata of the packet record differs in name capitalization from the record in our cache // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. + // <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing rr->resrec.rroriginalttl = 0; + rr->TimeRcvd = m->timenow; rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTime(m, rr); + SetNextCacheCheckTimeForRecord(m, rr); + LogInfo("Discarding due to domainname case change old: %s", CRDisplayString(m,rr)); + LogInfo("Discarding due to domainname case change new: %s", CRDisplayString(m,&m->rec.r)); + LogInfo("Discarding due to domainname case change in %d slot %3d in %d %d", + NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow); // DO NOT break out here -- we want to continue as if we never found it } else if (m->rec.r.resrec.rroriginalttl > 0) { + DNSQuestion *q; //if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr)); RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); + + // We have to reset the question interval to MaxQuestionInterval so that we don't keep + // polling the network once we get a valid response back. For the first time when a new + // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. + // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server + // configuration changed, without flushing the cache, we reset the question interval here. + // Currently, we do this for for both multicast and unicast questions as long as the record + // type is unique. For unicast, resource record is always unique and for multicast it is + // true for records like A etc. but not for PTR. + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + for (q = m->Questions; q; q=q->next) + { + if (!q->DuplicateOf && !q->LongLived && + ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->ThisQInterval = MaxQuestionInterval; + q->RequestUnicast = mDNSfalse; + q->unansweredQueries = 0; + debugf("mDNSCoreReceiveResponse: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + break; // Why break here? Aren't there other questions we might want to look at?-- SC July 2010 + } + } + } break; } else @@ -6515,10 +6071,17 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. + // If record's current expiry time is more than a second from now, we set it to expire in one second. + // If the record is already going to expire in less than one second anyway, we leave it alone -- + // we don't want to let the goodbye packet *extend* the record's lifetime in our cache. debugf("DE for %s", CRDisplayString(m, rr)); - rr->resrec.rroriginalttl = 1; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTime(m, rr); + if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond) + { + rr->resrec.rroriginalttl = 1; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + } break; } } @@ -6528,9 +6091,19 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // (unless it is just a deletion of a record we never had, in which case we don't care) if (!rr && m->rec.r.resrec.rroriginalttl > 0) { - rr = CreateNewCacheEntry(m, slot, cg); - if (rr && (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) && LLQType != uDNS_LLQ_Events) - { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } + const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events); + const mDNSs32 delay = AddToCFList ? NonZeroTime(m->timenow + mDNSPlatformOneSecond) : + CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot); + // If unique, assume we may have to delay delivery of this 'add' event. + // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd() + // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime() + // to schedule an mDNS_Execute task at the appropriate time. + rr = CreateNewCacheEntry(m, slot, cg, delay); + if (rr) + { + if (AddToCFList) { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } + else if (rr->DelayDelivery) ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery); + } } } m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it @@ -6563,7 +6136,9 @@ exit: // To avoid this, we need to ensure that the cache flushing operation will only act to // *decrease* a record's remaining lifetime, never *increase* it. for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next) - if (r1->resrec.InterfaceID == r2->resrec.InterfaceID && + // For Unicast (null InterfaceID) the DNSservers should also match + if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) && + (r1->resrec.InterfaceID || (r1->resrec.rDNSServer == r2->resrec.rDNSServer)) && r1->resrec.rrtype == r2->resrec.rrtype && r1->resrec.rrclass == r2->resrec.rrclass) { @@ -6573,7 +6148,7 @@ exit: { // If we find mismatched TTLs in an RRSet, correct them. // We only do this for records with a TTL of 2 or higher. It's possible to have a - // goodbye announcement with the cache flush bit set (or a case change on record rdata, + // goodbye announcement with the cache flush bit set (or a case-change on record rdata, // which we treat as a goodbye followed by an addition) and in that case it would be // inappropriate to synchronize all the other records to a TTL of 0 (or 1). // We suppress the message for the specific case of correcting from 240 to 60 for type TXT, @@ -6594,7 +6169,8 @@ exit: } else // else, if record is old, mark it to be flushed { - verbosedebugf("Cache flush %p X %p %s", r1, r2, CRDisplayString(m, r2)); + verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1)); + verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2)); // We set stale records to expire in one second. // This gives the owner a chance to rescue it if necessary. // This is important in the case of multi-homing and bridged networks: @@ -6611,12 +6187,13 @@ exit: // If a record is deleted twice, first with an explicit DE record, then a second time by virtue of the cache // flush bit on the new record replacing it, then we allow the record to be deleted immediately, without the usual // one-second grace period. This improves responsiveness for mDNS_Update(), as used for things like iChat status updates. - if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl <= 1 && r2->UnansweredQueries == MaxUnansweredQueries) + // <rdar://problem/5636422> Updating TXT records is too slow + // We check for "rroriginalttl == 1" because we want to include records tagged by the "packet TTL is zero" check above, + // which sets rroriginalttl to 1, but not records tagged by the rdata case-change check, which sets rroriginalttl to 0. + if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl == 1 && r2->UnansweredQueries == MaxUnansweredQueries) { - debugf("Cache flush for DE record %s", CRDisplayString(m, r2)); + LogInfo("Cache flush for DE record %s", CRDisplayString(m, r2)); r2->resrec.rroriginalttl = 0; - m->NextCacheCheck = m->timenow; - m->NextScheduledEvent = m->timenow; } else if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) { @@ -6629,13 +6206,15 @@ exit: // that we marked for deletion via an explicit DE record } } - SetNextCacheCheckTime(m, r2); + SetNextCacheCheckTimeForRecord(m, r2); } + if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to { - // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot); + // If no longer delaying, deliver answer now, else schedule delivery for the appropriate time if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); + else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery); } } @@ -6644,8 +6223,9 @@ exit: for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) { DNSQuestion q; + DNSQuestion *qptr = mDNSNULL; ptr = getQuestion(response, ptr, end, InterfaceID, &q); - if (ptr && (!dstaddr || ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q))) + if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr))) { // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft // Active Directory sites) we don't want to waste memory making negative cache entries for all the unicast answers. @@ -6658,17 +6238,17 @@ exit: // in conflict with the mDNS spec, because that spec says, "Multicast DNS Zones have no SOA record," so it's okay to cache // negative answers for "local. SOA" from a uDNS server, because the mDNS spec already says that such records do not exist :-) if (!InterfaceID && q.qtype != kDNSType_SOA && IsLocalDomain(&q.qname)) - LogInfo("Not generating negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + LogInfo("Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); else { CacheRecord *rr, *neg = mDNSNULL; mDNSu32 slot = HashSlot(&q.qname); CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, &q)) + if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) { // 1. If we got a fresh answer to this query, then don't need to generate a negative entry - if (rr->TimeRcvd + TicksTTL(rr) - m->timenow > 0) break; + if (RRExpireTime(rr) - m->timenow > 0) break; // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr; } @@ -6694,7 +6274,7 @@ exit: if (response->h.numAuthorities && (ptr = LocateAuthorities(response, end)) != mDNSNULL) { ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); - if (ptr && m->rec.r.resrec.rrtype == kDNSType_SOA) + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA) { const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data; mDNSu32 ttl_s = soa->min; @@ -6746,8 +6326,8 @@ exit: else while (1) { debugf("mDNSCoreReceiveResponse making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype)); - MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any); - CreateNewCacheEntry(m, slot, cg); + MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer); + CreateNewCacheEntry(m, slot, cg, 0); // We never need any delivery delay for these generated negative cache records m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it if (!repeat) break; repeat--; @@ -6762,6 +6342,20 @@ exit: } } +mDNSlocal void ScheduleWakeupForList(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e, AuthRecord *const thelist) + { + AuthRecord *rr; + for (rr = thelist; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && mDNSSameEthAddress(&rr->WakeUp.HMAC, e)) + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + } + +mDNSlocal void ScheduleWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e) + { + ScheduleWakeupForList(m, InterfaceID, e, m->DuplicateRecords); + ScheduleWakeupForList(m, InterfaceID, e, m->ResourceRecords); + } + mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus result) { if (result && result != mStatus_MemFree) @@ -6769,14 +6363,15 @@ mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus re if (result == mStatus_NameConflict) { - LogMsg("Received Conflicting mDNS -- waking %s %.6a %s", - InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); + LogMsg("Received Conflicting mDNS -- waking %s %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password); + ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); } else if (result == mStatus_MemFree) { m->ProxyRecords--; mDNSPlatformMemFree(ar); + mDNS_UpdateAllowSleep(m); } } @@ -6788,17 +6383,17 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, int i; AuthRecord opt; mDNSu8 *p = m->omsg.data; - OwnerOptData owner; + OwnerOptData owner = zeroOwner; // Need to zero this, so we'll know if this Update packet was missing its Owner option mDNSu32 updatelease = 0; const mDNSu8 *ptr; LogSPS("Received Update from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, - msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", - msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.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"); + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return; @@ -6809,7 +6404,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, if (ptr) { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); - if (ptr && m->rec.r.resrec.rrtype == kDNSType_OPT) + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) { const rdataOPT *o; const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; @@ -6860,7 +6455,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++) { ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); - if (ptr) + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) { mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec); AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem); @@ -6869,12 +6464,15 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, { mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared; m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet; + ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords); // Make sure we don't have any old stale duplicates of this record + ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords); mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, SPSRecordCallback, ar); AssignDomainName(&ar->namestorage, m->rec.r.resrec.name); ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse); ar->resrec.rdata->MaxRDLength = RDLengthMem; mDNSPlatformMemCopy(ar->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, RDLengthMem); - ar->WakeUp = owner; + ar->ForceMCast = mDNStrue; + ar->WakeUp = owner; if (m->rec.r.resrec.rrtype == kDNSType_PTR) { mDNSs32 t = ReverseMapDomainType(m->rec.r.resrec.name); @@ -6888,9 +6486,15 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, if (m->NextScheduledSPS - ar->TimeExpire > 0) m->NextScheduledSPS = ar->TimeExpire; mDNS_Register_internal(m, ar); - // For now, since we don't get IPv6 ND or data packets, we don't advertise AAAA records for our SPS clients - if (ar->resrec.rrtype == kDNSType_AAAA) ar->resrec.rroriginalttl = 0; + // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating, + // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited + // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower. + // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage + // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records. + if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6) + if (ar->resrec.rrtype == kDNSType_AAAA) ar->resrec.rroriginalttl = 0; m->ProxyRecords++; + mDNS_UpdateAllowSleep(m); LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar)); } } @@ -6928,7 +6532,7 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg if (ptr) { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); - if (ptr && m->rec.r.resrec.rrtype == kDNSType_OPT) + if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT) { const rdataOPT *o; const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength]; @@ -6958,7 +6562,7 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg } mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, - const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID) + const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver) { if (cr == &m->rec.r && m->rec.r.resrec.RecordType) { @@ -6971,6 +6575,7 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, // Create empty resource record cr->resrec.RecordType = kDNSRecordTypePacketNegative; cr->resrec.InterfaceID = InterfaceID; + cr->resrec.rDNSServer = dnsserver; cr->resrec.name = name; // Will be updated to point to cg->name when we call CreateNewCacheEntry cr->resrec.rrtype = rrtype; cr->resrec.rrclass = rrclass; @@ -7039,7 +6644,11 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co #endif #endif - if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; } + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) + { + LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + return; + } QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); // Read the integer parts which are in IETF byte-order (MSB first, LSB second) ptr = (mDNSu8 *)&msg->h.numQuestions; @@ -7074,15 +6683,15 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co else { LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)", - msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end-(mDNSu8 *)pkt, InterfaceID); + msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID); if (mDNS_LoggingEnabled) { int i = 0; - while (i<end-(mDNSu8 *)pkt) + while (i<end - (mDNSu8 *)pkt) { char buffer[128]; char *p = buffer + mDNS_snprintf(buffer, sizeof(buffer), "%04X", i); - do if (i<end-(mDNSu8 *)pkt) p += mDNS_snprintf(p, sizeof(buffer), " %02X", ((mDNSu8 *)pkt)[i]); while (++i & 15); + do if (i<end - (mDNSu8 *)pkt) p += mDNS_snprintf(p, sizeof(buffer), " %02X", ((mDNSu8 *)pkt)[i]); while (++i & 15); LogInfo("%s", buffer); } } @@ -7112,6 +6721,14 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co // and we have a key for company.com, so we try to locate the private query server for company.com, which necessarily entails // doing a standard DNS query for the _dns-query-tls._tcp SRV record for company.com. If we make the latter (public) query // a duplicate of the former (private) query, then it will block forever waiting for an answer that will never come. +// +// We keep SuppressUnusable questions separate so that we can return a quick response to them and not get blocked behind +// the queries that are not marked SuppressUnusable. But if the query is not suppressed, they are treated the same as +// non-SuppressUnusable questions. This should be fine as the goal of SuppressUnusable is to return quickly only if it +// is suppressed. If it is not suppressed, we do try all the DNS servers for valid answers like any other question. +// The main reason for this design is that cache entries point to a *single* question and that question is responsible +// for keeping the cache fresh as long as it is active. Having multiple active question for a single cache entry +// breaks this design principle. // If IsLLQ(Q) is true, it means the question is both: // (a) long-lived and @@ -7133,6 +6750,7 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest q->qclass == question->qclass && // class, IsLLQ(q) == IsLLQ(question) && // and long-lived status matches (!q->AuthInfo || question->AuthInfo) && // to avoid deadlock, don't make public query dup of a private one + (q->SuppressQuery == question->SuppressQuery) && // Questions that are suppressed/not suppressed q->qnamehash == question->qnamehash && SameDomainName(&q->qname, &question->qname)) // and name return(q); @@ -7161,7 +6779,10 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi q->servAddr = question->servAddr; q->servPort = question->servPort; q->qDNSServer = question->qDNSServer; + q->validDNSServers = question->validDNSServers; q->unansweredQueries = question->unansweredQueries; + q->noServerResponse = question->noServerResponse; + q->triedAllServersOnce = question->triedAllServersOnce; q->TargetQID = question->TargetQID; q->LocalSocket = question->LocalSocket; @@ -7199,22 +6820,232 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi } } -// Look up a DNS Server, matching by name in split-dns configurations. -mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name) +mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server) + { + mDNSs32 ptime = 0; + if (server->penaltyTime != 0) + { + ptime = server->penaltyTime - m->timenow; + if (ptime < 0) + { + // This should always be a positive value between 0 and DNSSERVER_PENALTY_TIME + // If it does not get reset in ResetDNSServerPenalties for some reason, we do it + // here + LogMsg("PenaltyTimeForServer: PenaltyTime negative %d, (server penaltyTime %d, timenow %d) resetting the penalty", + ptime, server->penaltyTime, m->timenow); + server->penaltyTime = 0; + ptime = 0; + } + } + return ptime; + } + +//Checks to see whether the newname is a better match for the name, given the best one we have +//seen so far (given in bestcount). +//Returns -1 if the newname is not a better match +//Returns 0 if the newname is the same as the old match +//Returns 1 if the newname is a better match +mDNSlocal int BetterMatchForName(const domainname *name, int namecount, const domainname *newname, int newcount, + int bestcount) + { + // If the name contains fewer labels than the new server's domain or the new name + // contains fewer labels than the current best, then it can't possibly be a better match + if (namecount < newcount || newcount < bestcount) return -1; + + // If there is no match, return -1 and the caller will skip this newname for + // selection + // + // If we find a match and the number of labels is the same as bestcount, then + // we return 0 so that the caller can do additional logic to pick one of + // the best based on some other factors e.g., penaltyTime + // + // If we find a match and the number of labels is more than bestcount, then we + // return 1 so that the caller can pick this over the old one. + // + // Note: newcount can either be equal or greater than bestcount beause of the + // check above. + + if (SameDomainName(SkipLeadingLabels(name, namecount - newcount), newname)) + return bestcount == newcount ? 0 : 1; + else + return -1; + } + +// Sets all the Valid DNS servers for a question +mDNSexport void SetValidDNSServers(mDNS *m, DNSQuestion *question) + { + DNSServer *curmatch = mDNSNULL; + int bestmatchlen = -1, namecount = CountLabels(&question->qname); + DNSServer *curr; + int bettermatch, currcount; + int index = 0; + + question->validDNSServers = zeroOpaque64; + for (curr = m->DNSServers; curr; curr = curr->next) + { + debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped); + // skip servers that will soon be deleted + if (curr->flags & DNSServer_FlagDelete) + { debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; } + + currcount = CountLabels(&curr->domain); + if ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) || (curr->interface == question->InterfaceID)) + { + bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); + + // If we found a better match (bettermatch == 1) then clear all the bits + // corresponding to the old DNSServers that we have may set before and start fresh. + // If we find an equal match, then include that DNSServer also by setting the corresponding + // bit + if ((bettermatch == 1) || (bettermatch == 0)) + { + curmatch = curr; + bestmatchlen = currcount; + if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; } + debugf("SetValidDNSServers: Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d", &curr->addr, curr->domain.c, curr->scoped, index); + bit_set_opaque64(question->validDNSServers, index); + } + } + index++; + } + question->noServerResponse = 0; + debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x for question %p %##s (%s)", + question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype)); + } + +// Get the Best server that matches a name. If you find penalized servers, look for the one +// that will come out of the penalty box soon +mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSOpaque64 validBits, int *selected, mDNSBool nameMatch) + { + DNSServer *curmatch = mDNSNULL; + int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0; + DNSServer *curr; + mDNSs32 bestPenaltyTime, currPenaltyTime; + int bettermatch, currcount; + int index = 0; + int currindex = -1; + + debugf("GetBestServer: ValidDNSServer bits 0x%x%x", validBits.l[1], validBits.l[0]); + bestPenaltyTime = DNSSERVER_PENALTY_TIME + 1; + for (curr = m->DNSServers; curr; curr = curr->next) + { + // skip servers that will soon be deleted + if (curr->flags & DNSServer_FlagDelete) + { debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; } + + // Check if this is a valid DNSServer + if (!bit_get_opaque64(validBits, index)) { debugf("GetBestServer: continuing for index %d", index); index++; continue; } + + currcount = CountLabels(&curr->domain); + currPenaltyTime = PenaltyTimeForServer(m, curr); + + debugf("GetBestServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d", + &curr->addr, curr->domain.c, curr->penaltyTime, currPenaltyTime); + + // If there are multiple best servers for a given question, we will pick the first one + // if none of them are penalized. If some of them are penalized in that list, we pick + // the least penalized one. BetterMatchForName walks through all best matches and + // "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server + // in the list when there are no penalized servers and least one among them + // when there are some penalized servers + // + // Notes on InterfaceID matching: + // + // 1) A DNSServer entry may have an InterfaceID but the scoped flag may not be set. This + // is the old way of specifying an InterfaceID option for DNSServer. We recoginize these + // entries by "scoped" being false. These are like any other unscoped entries except that + // if it is picked e.g., domain match, when the packet is sent out later, the packet will + // be sent out on that interface. Theese entries can be matched by either specifying a + // zero InterfaceID or non-zero InterfaceID on the question. Specifying an InterfaceID on + // the question will cause an extra check on matching the InterfaceID on the question + // against the DNSServer. + // + // 2) A DNSServer may also have both scoped set and InterfaceID non-NULL. This + // is the new way of specifying an InterfaceID option for DNSServer. These will be considered + // only when the question has non-zero interfaceID. + + if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID)) + { + + // If we know that all the names are already equally good matches, then skip calling BetterMatchForName. + // This happens when we initially walk all the DNS servers and set the validity bit on the question. + // Actually we just need PenaltyTime match, but for the sake of readability we just skip the expensive + // part and still do some redundant steps e.g., InterfaceID match + + if (nameMatch) bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen); + else bettermatch = 0; + + // If we found a better match (bettermatch == 1) then we don't need to + // compare penalty times. But if we found an equal match, then we compare + // the penalty times to pick a better match + + if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime)) + { currindex = index; curmatch = curr; bestmatchlen = currcount; bestPenaltyTime = currPenaltyTime; } + } + index++; + } + if (selected) *selected = currindex; + return curmatch; + } + +// Look up a DNS Server, matching by name and InterfaceID +mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID) + { + DNSServer *curmatch = mDNSNULL; + char *ifname = mDNSNULL; // for logging purposes only + mDNSOpaque64 allValid; + + if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) + InterfaceID = mDNSNULL; + + if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); + + // By passing in all ones, we make sure that every DNS server is considered + allValid.l[0] = allValid.l[1] = 0xFFFFFFFF; + + curmatch = GetBestServer(m, name, InterfaceID, allValid, mDNSNULL, mDNStrue); + + if (curmatch != mDNSNULL) + LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr, + mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", + InterfaceID, name); + else + LogInfo("GetServerForName: no DNS server (Scope %s:%p) found for name %##s", ifname ? ifname : "None", InterfaceID, name); + + return(curmatch); + } + +// Look up a DNS Server for a question within its valid DNSServer bits +mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question) { - DNSServer *curmatch = mDNSNULL, *p; - int curmatchlen = -1, ncount = name ? CountLabels(name) : 0; + DNSServer *curmatch = mDNSNULL; + char *ifname = mDNSNULL; // for logging purposes only + mDNSInterfaceID InterfaceID = question->InterfaceID; + const domainname *name = &question->qname; + int currindex; - for (p = m->DNSServers; p; p = p->next) + if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) + InterfaceID = mDNSNULL; + + if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); + + if (!mDNSOpaque64IsZero(&question->validDNSServers)) { - int scount = CountLabels(&p->domain); - if (!(p->flags & DNSServer_FlagDelete) && ncount >= scount && scount > curmatchlen) - if (SameDomainName(SkipLeadingLabels(name, ncount - scount), &p->domain)) - { curmatch = p; curmatchlen = scount; } + curmatch = GetBestServer(m, name, InterfaceID, question->validDNSServers, &currindex, mDNSfalse); + if (currindex != -1) bit_clr_opaque64(question->validDNSServers, currindex); } + + if (curmatch != mDNSNULL) + LogInfo("GetServerForQuestion: %p DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s (%s)", question, &curmatch->addr, + mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", + InterfaceID, name, DNSTypeName(question->qtype)); + else + LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p) found for name %##s (%s)", question, ifname ? ifname : "None", InterfaceID, name, DNSTypeName(question->qtype)); + return(curmatch); } + #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \ (mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort))) @@ -7224,7 +7055,7 @@ mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n) DNSQuestion *q; (void)n; // Unused mDNS_Lock(m); - LogInfo("LLQNATCallback external address:port %.4a:%u", &n->ExternalAddress, mDNSVal16(n->ExternalPort)); + LogInfo("LLQNATCallback external address:port %.4a:%u, NAT result %d", &n->ExternalAddress, mDNSVal16(n->ExternalPort), n->Result); for (q = m->Questions; q; q=q->next) if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) startLLQHandshake(m, q); // If ExternalPort is zero, will do StartLLQPolling instead @@ -7234,6 +7065,212 @@ mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n) mDNS_Unlock(m); } +mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, domainname *qname, mDNSu16 qtype, mDNSInterfaceID InterfaceID) + { + NetworkInterfaceInfo *i; + mDNSs32 iptype; + DomainAuthInfo *AuthInfo; + + if (qtype == kDNSType_A) iptype = mDNSAddrType_IPv4; + else if (qtype == kDNSType_AAAA) iptype = mDNSAddrType_IPv6; + else { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", qname, DNSTypeName(qtype)); return mDNSfalse; } + + // We still want the ability to be able to listen to the local services and hence + // don't fail .local requests. We always have a loopback interface which we don't + // check here. + if (InterfaceID != mDNSInterface_Unicast && IsLocalDomain(qname)) { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", qname, DNSTypeName(qtype)); return mDNSfalse; } + + // Skip Private domains as we have special addresses to get the hosts in the Private domain + AuthInfo = GetAuthInfoForName_internal(m, qname); + if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel) + { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Private Domain", qname, DNSTypeName(qtype)); return mDNSfalse; } + + // Match on Type, Address and InterfaceID + // + // Check whether we are looking for a name that ends in .local, then presence of a link-local + // address on the interface is sufficient. + for (i = m->HostInterfaces; i; i = i->next) + { + if (i->ip.type != iptype) continue; + + if (!InterfaceID || (InterfaceID == mDNSInterface_LocalOnly) || (InterfaceID == mDNSInterface_P2P) || + (InterfaceID == mDNSInterface_Unicast) || (i->InterfaceID == InterfaceID)) + { + if (iptype == mDNSAddrType_IPv4 && !mDNSv4AddressIsLoopback(&i->ip.ip.v4) && !mDNSv4AddressIsLinkLocal(&i->ip.ip.v4)) + { + LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.4a found", qname, DNSTypeName(qtype), + &i->ip.ip.v4); + return mDNSfalse; + } + else if (iptype == mDNSAddrType_IPv6 && + !mDNSv6AddressIsLoopback(&i->ip.ip.v6) && + !mDNSv6AddressIsLinkLocal(&i->ip.ip.v6) && + !mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelHostAddr) && + !mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelRelayAddr)) + { + LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.16a found", qname, DNSTypeName(qtype), + &i->ip.ip.v6); + return mDNSfalse; + } + } + } + LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, because no matching interface found", qname, DNSTypeName(qtype)); + return mDNStrue; + } + +mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q) + { + CacheRecord *rr; + mDNSu32 slot; + CacheGroup *cg; + + // Temporarily turn off suppression so that AnswerCurrentQuestionWithResourceRecord + // can answer the question + q->SuppressQuery = mDNSfalse; + slot = HashSlot(&q->qname); + cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + // Don't deliver RMV events for negative records + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) + { + LogInfo("CheckSuppressedCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d", + CRDisplayString(m, rr), q, q->qname.c, DNSTypeName(q->qtype), rr->CRActiveQuestion, q->CurrentAnswers); + continue; + } + + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + LogInfo("CheckSuppressedCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s", + q->qname.c, CRDisplayString(m, rr)); + + q->CurrentAnswers--; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + + if (rr->CRActiveQuestion == q) + { + DNSQuestion *qptr; + // If this was the active question for this cache entry, it was the one that was + // responsible for keeping the cache entry fresh when the cache entry was reaching + // its expiry. We need to handover the responsibility to someone else. Otherwise, + // when the cache entry is about to expire, we won't find an active question + // (pointed by CRActiveQuestion) to refresh the cache. + for (qptr = m->Questions; qptr; qptr=qptr->next) + if (ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr)) + break; + + if (qptr) + LogInfo("CheckSuppressedCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, " + "Original question CurrentAnswers %d, new question CurrentAnswers %d, SuppressUnusable %d, SuppressQuery %d", + qptr, CRDisplayString(m,rr), q->CurrentAnswers, qptr->CurrentAnswers, qptr->SuppressUnusable, qptr->SuppressQuery); + + rr->CRActiveQuestion = qptr; // Question used to be active; new value may or may not be null + if (!qptr) m->rrcache_active--; // If no longer active, decrement rrcache_active count + } + AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here + } + } + if (m->CurrentQuestion == q) q->SuppressQuery = mDNStrue; + } + +mDNSlocal mDNSBool IsQuestionNew(mDNS *const m, DNSQuestion *question) + { + DNSQuestion *q; + for (q = m->NewQuestions; q; q = q->next) + if (q == question) return mDNStrue; + return mDNSfalse; + } + +// The caller should hold the lock +mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) + { + DNSQuestion *q, *qnext; + DNSQuestion *restart = mDNSNULL; + + // We look through all questions including new questions. During network change events, + // we potentially restart questions here in this function that ends up as new questions, + // which may be suppressed at this instance. Before it is handled we get another network + // event that changes the status e.g., address becomes available. If we did not process + // new questions, we would never change its SuppressQuery status. + for (q = m->Questions; q ; q = qnext) + { + qnext = q->next; + if (!mDNSOpaque16IsZero(q->TargetQID) && q->SuppressUnusable) + { + mDNSBool old = q->SuppressQuery; + q->SuppressQuery = ShouldSuppressQuery(m, &q->qname, q->qtype, q->InterfaceID); + if (q->SuppressQuery != old) + { + if (q->SuppressQuery) + { + // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before + // followed by a negative cache response + if (m->CurrentQuestion) + LogMsg("CheckSuppressUnusableQuestions: ERROR m->CurrentQuestion already set: %##s (%s)", + m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); + + // If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events. + if (!IsQuestionNew(m, q)) + { + m->CurrentQuestion = q; + CheckSuppressedCurrentQuestion(m, q); + if (m->CurrentQuestion != q) + { + m->CurrentQuestion = mDNSNULL; + LogInfo("CheckSuppressUnusableQuestions: Question deleted while giving RMV events"); + continue; + } + m->CurrentQuestion = mDNSNULL; + } + else { debugf("CheckSuppressUnusableQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); } + } + + // There are two cases here. + // + // 1. Previously it was suppressed and now it is not suppressed, restart the question so + // that it will start as a new question. Note that we can't just call ActivateUnicastQuery + // because when we get the response, if we had entries in the cache already, it will not answer + // this question if the cache entry did not change. Hence, we need to restart + // the query so that it can be answered from the cache. + // + // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions + // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question + // is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false). + // A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed + // (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an + // immediate response and not want to be blocked behind a question that is querying DNS servers. + // When the question is not suppressed, we don't want two active questions sending packets on the wire. + // This affects both efficiency and also the current design where there is only one active question + // pointed to from a cache entry. + // + // We restart queries in a two step process by first calling stop and build a temporary list which we + // will restart at the end. The main reason for the two step process is to handle duplicate questions. + // If there are duplicate questions, calling stop inherits the values from another question on the list (which + // will soon become the real question) including q->ThisQInterval which might be zero if it was + // suppressed before. At the end when we have restarted all questions, none of them is active as each + // inherits from one another and we need to reactivate one of the questions here which is a little hacky. + // + // It is much cleaner and less error prone to build a list of questions and restart at the end. + + LogInfo("CheckSuppressUnusableQuestions: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StopQuery_internal(m, q); + q->next = restart; + restart = q; + } + } + } + while (restart) + { + q = restart; + restart = restart->next; + q->next = mDNSNULL; + LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StartQuery_internal(m, q); + } + } + mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) { if (question->Target.type && !ValidQuestionTarget(question)) @@ -7243,13 +7280,11 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu question->Target.type = mDNSAddrType_None; } - if (!question->Target.type) question->TargetPort = zeroIPPort; // If question->Target specified clear TargetPort + if (!question->Target.type) question->TargetPort = zeroIPPort; // If no question->Target specified clear TargetPort question->TargetQID = #ifndef UNICAST_DISABLED - (question->Target.type || (question->InterfaceID == mDNSInterface_Unicast) || - (question->InterfaceID != mDNSInterface_LocalOnly && !question->ForceMCast && !IsLocalDomain(&question->qname))) - ? mDNS_NewMessageID(m) : + (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : #endif // UNICAST_DISABLED zeroID; @@ -7270,7 +7305,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu // Note: It important that new questions are appended at the *end* of the list, not prepended at the start q = &m->Questions; - if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) q = &m->LocalOnlyQuestions; while (*q && *q != question) q=&(*q)->next; if (*q) @@ -7283,7 +7318,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu *q = question; // If this question is referencing a specific interface, verify it exists - if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast) + if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); if (!intf) @@ -7309,7 +7344,12 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu question->UniqueAnswers = 0; question->FlappingInterface1 = mDNSNULL; question->FlappingInterface2 = mDNSNULL; - question->AuthInfo = GetAuthInfoForQuestion(m, question); // Must do this before calling FindDuplicateQuestion() + // Must do AuthInfo and SuppressQuery before calling FindDuplicateQuestion() + question->AuthInfo = GetAuthInfoForQuestion(m, question); + if (question->SuppressUnusable) + question->SuppressQuery = ShouldSuppressQuery(m, &question->qname, question->qtype, question->InterfaceID); + else + question->SuppressQuery = 0; question->DuplicateOf = FindDuplicateQuestion(m, question); question->NextInDQList = mDNSNULL; question->SendQNow = mDNSNULL; @@ -7323,6 +7363,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single // NAT mapping for receiving inbound add/remove events. question->LocalSocket = mDNSNULL; + question->deliverAddEvents = mDNSfalse; question->qDNSServer = mDNSNULL; question->unansweredQueries = 0; question->nta = mDNSNULL; @@ -7336,6 +7377,9 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu question->expire = 0; question->ntries = 0; question->id = zeroOpaque64; + question->validDNSServers = zeroOpaque64; + question->triedAllServersOnce = 0; + question->noServerResponse = 0; if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo; @@ -7344,11 +7388,15 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu debugf("mDNS_StartQuery: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow, - question->LastQTime + question->ThisQInterval - m->timenow, + NextQSendTime(question) - m->timenow, question->DelayAnswering ? question->DelayAnswering - m->timenow : 0, question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf); - if (question->InterfaceID == mDNSInterface_LocalOnly) + if (question->DelayAnswering) + LogInfo("mDNS_StartQuery_internal: Delaying answering for %d ticks while cache stabilizes for %##s (%s)", + question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype)); + + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) { if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; } @@ -7364,7 +7412,27 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu // this routine with the question list data structures in an inconsistent state. if (!mDNSOpaque16IsZero(question->TargetQID)) { - question->qDNSServer = GetServerForName(m, &question->qname); + // Duplicate questions should have the same DNSServers so that when we find + // a matching resource record, all of them get the answers. Calling GetServerForQuestion + // for the duplicate question may get a different DNS server from the original question + if (question->DuplicateOf) + { + question->validDNSServers = question->DuplicateOf->validDNSServers; + question->qDNSServer = question->DuplicateOf->qDNSServer; + LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), DNS Server %#a:%d", + question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); + } + else + { + SetValidDNSServers(m, question); + question->qDNSServer = GetServerForQuestion(m, question); + LogInfo("mDNS_StartQuery_internal: question %p %##s (%s), DNS Server %#a:%d", + question, question->qname.c, DNSTypeName(question->qtype), + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); + } ActivateUnicastQuery(m, question, mDNSfalse); // If long-lived query, and we don't have our NAT mapping active, start it now @@ -7395,7 +7463,15 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu mDNSexport void CancelGetZoneData(mDNS *const m, ZoneData *nta) { debugf("CancelGetZoneData %##s (%s)", nta->question.qname.c, DNSTypeName(nta->question.qtype)); - mDNS_StopQuery_internal(m, &nta->question); + // This function may be called anytime to free the zone information.The question may or may not have stopped. + // If it was already stopped, mDNS_StopQuery_internal would have set q->ThisQInterval to -1 and should not + // call it again + if (nta->question.ThisQInterval != -1) + { + mDNS_StopQuery_internal(m, &nta->question); + if (nta->question.ThisQInterval != -1) + LogMsg("CancelGetZoneData: Question %##s (%s) ThisQInterval %d not -1", nta->question.qname.c, DNSTypeName(nta->question.qtype), nta->question.ThisQInterval); + } mDNSPlatformMemFree(nta); } @@ -7408,7 +7484,7 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que //LogInfo("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - if (question->InterfaceID == mDNSInterface_LocalOnly) qp = &m->LocalOnlyQuestions; + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) qp = &m->LocalOnlyQuestions; while (*qp && *qp != question) qp=&(*qp)->next; if (*qp) *qp = (*qp)->next; else @@ -7436,10 +7512,14 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que if (rr->CRActiveQuestion == question) { DNSQuestion *q; + // Checking for ActiveQuestion filters questions that are suppressed also + // as suppressed questions are not active for (q = m->Questions; q; q=q->next) // Scan our list of questions if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) break; - debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s", q, CRDisplayString(m,rr)); + if (q) + debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s, Original question CurrentAnswers %d, new question " + "CurrentAnswers %d, SuppressQuery %d", q, CRDisplayString(m,rr), question->CurrentAnswers, q->CurrentAnswers, q->SuppressQuery); rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count } @@ -7473,7 +7553,6 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que // so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already // invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary // *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query. - if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; } if (question->LocalSocket) { mDNSPlatformUDPClose(question->LocalSocket); question->LocalSocket = mDNSNULL; } if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived) @@ -7513,6 +7592,8 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que UpdateAutoTunnelDomainStatuses(m); #endif } + // wait until we send the refresh above which needs the nta + if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } return(mStatus_NoError); } @@ -7598,22 +7679,15 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu question->Target = zeroAddr; question->qtype = kDNSType_PTR; question->qclass = kDNSClass_IN; - question->LongLived = mDNSfalse; + question->LongLived = mDNStrue; question->ExpectUnique = mDNSfalse; question->ForceMCast = ForceMCast; question->ReturnIntermed = mDNSfalse; + question->SuppressUnusable = mDNSfalse; question->QuestionCallback = Callback; question->QuestionContext = Context; if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); -#ifndef UNICAST_DISABLED - if (Question_uDNS(question)) - { - question->LongLived = mDNStrue; - question->ThisQInterval = InitialQuestionInterval; - question->LastQTime = m->timenow - question->ThisQInterval; - } -#endif // UNICAST_DISABLED return(mDNS_StartQuery_internal(m, question)); } @@ -7782,6 +7856,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qSRV.ExpectUnique = mDNStrue; query->qSRV.ForceMCast = mDNSfalse; query->qSRV.ReturnIntermed = mDNSfalse; + query->qSRV.SuppressUnusable = mDNSfalse; query->qSRV.QuestionCallback = FoundServiceInfoSRV; query->qSRV.QuestionContext = query; @@ -7795,6 +7870,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qTXT.ExpectUnique = mDNStrue; query->qTXT.ForceMCast = mDNSfalse; query->qTXT.ReturnIntermed = mDNSfalse; + query->qTXT.SuppressUnusable = mDNSfalse; query->qTXT.QuestionCallback = FoundServiceInfoTXT; query->qTXT.QuestionContext = query; @@ -7808,6 +7884,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv4.ExpectUnique = mDNStrue; query->qAv4.ForceMCast = mDNSfalse; query->qAv4.ReturnIntermed = mDNSfalse; + query->qAv4.SuppressUnusable = mDNSfalse; query->qAv4.QuestionCallback = FoundServiceInfo; query->qAv4.QuestionContext = query; @@ -7821,6 +7898,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv6.ExpectUnique = mDNStrue; query->qAv6.ForceMCast = mDNSfalse; query->qAv6.ReturnIntermed = mDNSfalse; + query->qAv6.SuppressUnusable = mDNSfalse; query->qAv6.QuestionCallback = FoundServiceInfo; query->qAv6.QuestionContext = query; @@ -7870,6 +7948,7 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->ExpectUnique = mDNSfalse; question->ForceMCast = mDNSfalse; question->ReturnIntermed = mDNSfalse; + question->SuppressUnusable = mDNSfalse; question->QuestionCallback = Callback; question->QuestionContext = Context; if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); @@ -7897,12 +7976,6 @@ mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) { -#ifndef UNICAST_DISABLED - mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name)); -#else - mDNSBool unicast = mDNSfalse; -#endif - if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); @@ -7914,37 +7987,36 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt // If TTL is unspecified, leave TTL unchanged if (newttl == 0) newttl = rr->resrec.rroriginalttl; - // If we already have an update queued up which has not gone through yet, - // give the client a chance to free that memory - if (!unicast && rr->NewRData) + // 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 ... + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... if (rr->UpdateCallback) - rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary + rr->UpdateCallback(m, rr, n, rr->newrdlength); // ...and let the client free this memory, if necessary } rr->NewRData = newrdata; rr->newrdlength = newrdlength; rr->UpdateCallback = Callback; - if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); } +#ifndef UNICAST_DISABLED + if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P && !IsLocalDomain(rr->resrec.name)) + { + mStatus status = uDNS_UpdateRecord(m, rr); + // The caller frees the memory on error, don't retain stale pointers + if (status != mStatus_NoError) { rr->NewRData = mDNSNULL; rr->newrdlength = 0; } + mDNS_Unlock(m); + return(status); + } +#endif if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)) CompleteRDataUpdate(m, rr); else { - domainlabel name; - domainname type, domain; - DeconstructServiceName(rr->resrec.name, &name, &type, &domain); rr->AnnounceCount = InitialAnnounceCount; - // iChat often does suprious record updates where no data has changed. For the _presence service type, using - // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful - // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data - // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us. - // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement. - if (SameDomainLabel(type.c, (mDNSu8*)"\x6_ichat")) rr->AnnounceCount = 1; InitializeLastAPTime(m, rr); while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; @@ -8176,22 +8248,16 @@ mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *act mDNSlocal void RestartRecordGetZoneData(mDNS * const m) { AuthRecord *rr; - ServiceRecordSet *s; - + LogInfo("RestartRecordGetZoneData: ResourceRecords"); for (rr = m->ResourceRecords; rr; rr=rr->next) - if (AuthRecord_uDNS(rr)) + if (AuthRecord_uDNS(rr) && rr->state != regState_NoTarget) { debugf("RestartRecordGetZoneData: StartGetZoneData for %##s", rr->resrec.name->c); - if (rr->nta) CancelGetZoneData(m, rr->nta); + // Zero out the updateid so that if we have a pending response from the server, it won't + // be accepted as a valid response. If we accept the response, we might free the new "nta" + if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); } rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr); } - - for (s = m->ServiceRegistrations; s; s = s->uDNS_next) - { - debugf("RestartRecordGetZoneData: StartGetZoneData for %##s", s->RR_SRV.resrec.name->c); - if (s->srs_nta) CancelGetZoneData(m, s->srs_nta); - s->srs_nta = StartGetZoneData(m, s->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, s); - } } mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set) @@ -8236,7 +8302,12 @@ mDNSexport void mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceI for (i=0; i<3; i++) if (set->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery_internal(m, &set->NetWakeResolve[i]); // Make special call to the browse callback to let it know it can to remove all records for this interface - if (m->SPSBrowseCallback) m->SPSBrowseCallback(m, &set->NetWakeBrowse, mDNSNULL, mDNSfalse); + if (m->SPSBrowseCallback) + { + mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback + m->SPSBrowseCallback(m, &set->NetWakeBrowse, mDNSNULL, mDNSfalse); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } // Reset our variables back to initial state, so we're ready for when NetWake is turned back on // (includes resetting NetWakeBrowse.ThisQInterval back to -1) @@ -8260,8 +8331,8 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s // Assume this interface will be active now, unless we find a duplicate already in the list set->InterfaceActive = mDNStrue; - set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); - set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); + set->IPv4Available = (mDNSu8)(set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); + set->IPv6Available = (mDNSu8)(set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); InitializeNetWakeState(m, set); @@ -8307,37 +8378,40 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive)) { DNSQuestion *q; - // If flapping, delay between first and second queries is eight seconds instead of one - mDNSs32 delay = flapping ? mDNSPlatformOneSecond * 5 : 0; - mDNSu8 announce = flapping ? (mDNSu8)1 : InitialAnnounceCount; - mDNSs32 newSS = 0; + // Normally, after an interface comes up, we pause half a second before beginning probing. + // This is to guard against cases where there's rapid interface changes, where we could be confused by + // seeing packets we ourselves sent just moments ago (perhaps when this interface had a different address) + // which are then echoed back after a short delay by some Ethernet switches and some 802.11 base stations. + // We don't want to do a probe, and then see a stale echo of an announcement we ourselves sent, + // and think it's a conflicting answer to our probe. + // In the case of a flapping interface, we pause for five seconds, and reduce the announcement count to one packet. + const mDNSs32 probedelay = flapping ? mDNSPlatformOneSecond * 5 : mDNSPlatformOneSecond / 2; + const mDNSu8 numannounce = flapping ? (mDNSu8)1 : InitialAnnounceCount; // Use a small amount of randomness: // In the case of a network administrator turning on an Ethernet hub so that all the // connected machines establish link at exactly the same time, we don't want them all // to go and hit the network with identical queries at exactly the same moment. - newSS = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); -#if APPLE_OSX_mDNSResponder - // We set this to at least 2 seconds, because the MacOSX platform layer typically gets lots - // of network change notifications in a row, and we don't know when we're done getting notified. - // Note that this will not be set if the interface doesn't do multicast (set->McastTxRx). - newSS += mDNSPlatformOneSecond * 2; -#endif - if (!m->SuppressSending || newSS - m->SuppressSending < 0) m->SuppressSending = newSS; - - if (flapping) - { - LogMsg("Note: RegisterInterface: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect", - set->ifname, &set->ip); - if (!m->SuppressProbes || - m->SuppressProbes - (m->timenow + delay) < 0) - m->SuppressProbes = (m->timenow + delay); - } + // We set a random delay of up to InitialQuestionInterval (1/3 second). + // We must *never* set m->SuppressSending to more than that (or set it repeatedly in a way + // that causes mDNSResponder to remain in a prolonged state of SuppressSending, because + // suppressing packet sending for more than about 1/3 second can cause protocol correctness + // to start to break down (e.g. we don't answer probes fast enough, and get name conflicts). + // See <rdar://problem/4073853> mDNS: m->SuppressSending set too enthusiastically + if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); + + if (flapping) LogMsg("RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + + LogInfo("RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay); + if (m->SuppressProbes == 0 || + m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0) + m->SuppressProbes = NonZeroTime(m->timenow + probedelay); for (q = m->Questions; q; q=q->next) // Scan our list of questions if (mDNSOpaque16IsZero(q->TargetQID)) if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, { // then reactivate this question + // If flapping, delay between first and second queries is nine seconds instead of one second mDNSBool dodelay = flapping && (q->FlappingInterface1 == set->InterfaceID || q->FlappingInterface2 == set->InterfaceID); mDNSs32 initial = dodelay ? InitialQuestionInterval * QuestionIntervalStep2 : InitialQuestionInterval; mDNSs32 qdelay = dodelay ? mDNSPlatformOneSecond * 5 : 0; @@ -8361,13 +8435,18 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s { if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - if (rr->AnnounceCount < announce) rr->AnnounceCount = announce; + if (rr->AnnounceCount < numannounce) rr->AnnounceCount = numannounce; + rr->SendNSECNow = mDNSNULL; InitializeLastAPTime(m, rr); } } RestartRecordGetZoneData(m); + CheckSuppressUnusableQuestions(m); + + mDNS_UpdateAllowSleep(m); + mDNS_Unlock(m); return(mStatus_NoError); } @@ -8437,9 +8516,8 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;" " marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip); - if (flapping) - LogMsg("Note: DeregisterInterface: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect", - set->ifname, &set->ip); + if (set->McastTxRx && flapping) + LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); // 1. Deactivate any questions specific to this interface, and tag appropriate questions // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them @@ -8460,15 +8538,16 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se { // If this interface is deemed flapping, // postpone deleting the cache records in case the interface comes back again - if (!flapping) mDNS_PurgeCacheResourceRecord(m, rr); - else + if (set->McastTxRx && flapping) { - // We want these record to go away in 30 seconds + // For a flapping interface we want these record to go away after 30 seconds + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); // We set UnansweredQueries = MaxUnansweredQueries so we don't waste time doing any queries for them -- // if the interface does come back, any relevant questions will be reactivated anyway - mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); rr->UnansweredQueries = MaxUnansweredQueries; } + else + mDNS_PurgeCacheResourceRecord(m, rr); } // 3. Any DNS servers specific to this interface are now unusable @@ -8493,12 +8572,15 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se mDNSu32 slot; CacheGroup *cg; CacheRecord *rr; - m->NextCacheCheck = m->timenow; FORALL_CACHERECORDS(slot, cg, rr) if (rr->resrec.InterfaceID == set->InterfaceID) mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); } + CheckSuppressUnusableQuestions(m); + + mDNS_UpdateAllowSleep(m); + mDNS_Unlock(m); } @@ -8534,24 +8616,27 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu // are still in the process of deregistering, don't pass on the NameConflict/MemFree message until // every record is finished cleaning up. mDNSu32 i; + ExtraResourceRecord *e = sr->Extras; + if (sr->RR_SRV.resrec.RecordType != kDNSRecordTypeUnregistered) return; if (sr->RR_TXT.resrec.RecordType != kDNSRecordTypeUnregistered) return; if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return; if (sr->RR_ADV.resrec.RecordType != kDNSRecordTypeUnregistered) return; for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return; + while (e) + { + if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return; + e = e->next; + } + // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, // then we can now report the NameConflict to the client if (sr->Conflict) result = mStatus_NameConflict; - if (sr->srs_nta) - { - LogMsg("ServiceCallback ERROR Got mStatus_MemFree with srs_nta still set for %s", ARDisplayString(m, &sr->RR_SRV)); - CancelGetZoneData(m, sr->srs_nta); - sr->srs_nta = mDNSNULL; - } } + LogInfo("ServiceCallback: All records %s for %##s", (result == mStatus_MemFree ? "Unregistered": "Registered"), sr->RR_PTR.resrec.name->c); // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback // function is allowed to do anything, including deregistering this service and freeing its memory. if (sr->ServiceCallback) @@ -8565,47 +8650,6 @@ mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) sr->ServiceCallback(m, sr, result); } -#if !defined(UNICAST_DISABLED) && USE_SEPARATE_UDNS_SERVICE_LIST -mDNSlocal mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs) - { - mDNSu32 i; - ServiceRecordSet **p = &m->ServiceRegistrations; - while (*p && *p != srs) p=&(*p)->uDNS_next; - if (*p) { LogMsg("uDNS_RegisterService: %p %##s already in list", srs, srs->RR_SRV.resrec.name->c); return(mStatus_AlreadyRegistered); } - - srs->uDNS_next = mDNSNULL; - *p = srs; - - srs->RR_SRV.resrec.rroriginalttl = kHostNameTTL; - srs->RR_TXT.resrec.rroriginalttl = kStandardTTL; - srs->RR_PTR.resrec.rroriginalttl = kStandardTTL; - for (i = 0; i < srs->NumSubTypes;i++) srs->SubTypes[i].resrec.rroriginalttl = kStandardTTL; - - srs->srs_uselease = mDNStrue; - - if (srs->RR_SRV.AutoTarget) - { - // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other - // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate, - // with the port number in our advertised SRV record automatically tracking the external mapped port. - DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name); - if (!AuthInfo || !AuthInfo->AutoTunnel) srs->RR_SRV.AutoTarget = Target_AutoHostAndNATMAP; - } - - if (!GetServiceTarget(m, &srs->RR_SRV)) - { - // defer registration until we've got a target - LogInfo("uDNS_RegisterService - no target for %##s", srs->RR_SRV.resrec.name->c); - srs->state = regState_NoTarget; - return mStatus_NoError; - } - - ActivateUnicastRegistration(m, &srs->RR_SRV); - srs->state = regState_FetchingZoneData; - return mStatus_NoError; - } -#endif - // Note: // Name is first label of domain name (any dots in the name are actual dots, not label separators) // Type is service type (e.g. "_ipp._tcp.") @@ -8624,22 +8668,6 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, mStatus err; mDNSu32 i; - sr->state = regState_Zero; - sr->srs_uselease = 0; - sr->TestForSelfConflict = 0; - sr->Private = 0; - sr->id = zeroID; - sr->zone.c[0] = 0; - sr->SRSUpdateServer = zeroAddr; - sr->SRSUpdatePort = zeroIPPort; - mDNSPlatformMemZero(&sr->NATinfo, sizeof(sr->NATinfo)); - sr->NATinfo.IntPort = port; // Record originally-requested port - sr->ClientCallbackDeferred = 0; - sr->DeferredStatus = 0; - sr->SRVUpdateDeferred = 0; - sr->SRVChanged = 0; - sr->tcp = mDNSNULL; - sr->ServiceCallback = Callback; sr->ServiceContext = Context; sr->Conflict = mDNSfalse; @@ -8678,6 +8706,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, // 2. Set up the PTR record rdata to point to our service name // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too + // Note: uDNS registration code assumes that Additional1 points to the SRV record AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name); sr->RR_PTR.Additional1 = &sr->RR_SRV; sr->RR_PTR.Additional2 = &sr->RR_TXT; @@ -8709,6 +8738,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, // 4. Set up the TXT record rdata, // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us + // Note: uDNS registration code assumes that DependentOn points to the SRV record if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0; else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c) { @@ -8718,28 +8748,10 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, } sr->RR_TXT.DependentOn = &sr->RR_SRV; - sr->srs_nta = mDNSNULL; - -#if !defined(UNICAST_DISABLED) && USE_SEPARATE_UDNS_SERVICE_LIST - // If the client has specified an explicit InterfaceID, - // then we do a multicast registration on that interface, even for unicast domains. - if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.namestorage))) - { - mStatus status; - mDNS_Lock(m); - // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, - // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". - // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. - // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck) - if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; } - - status = uDNS_RegisterService(m, sr); - mDNS_Unlock(m); - return(status); - } -#endif - mDNS_Lock(m); + // It is important that we register SRV first. uDNS assumes that SRV is registered first so + // that if the SRV cannot find a target, rest of the records that belong to this service + // will not be activated. err = mDNS_Register_internal(m, &sr->RR_SRV); if (!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 @@ -8757,14 +8769,6 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, return(err); } -mDNSlocal void DummyCallback(mDNS *const m, AuthRecord *rr, mStatus result) - { - (void)m; // Unused - (void)rr; // Unused - (void)result; // Unused - LogInfo("DummyCallback %d %s", result, ARDisplayString(m, rr)); - } - mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl) { @@ -8788,18 +8792,7 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, extra->r.resrec.name->c, DNSTypeName(extra->r.resrec.rrtype), extra->r.resrec.rdlength); status = mDNS_Register_internal(m, &extra->r); - if (status == mStatus_NoError) - { - *e = extra; -#ifndef UNICAST_DISABLED - if (AuthRecord_uDNS(&sr->RR_SRV)) - { - extra->r.resrec.RecordType = kDNSRecordTypeShared; // don't want it to conflict with the service name (???) - extra->r.RecordCallback = DummyCallback; // don't generate callbacks for extra RRs for unicast services (WHY NOT????) - if (sr->state != regState_Registered && sr->state != regState_Refresh) extra->r.state = regState_ExtraQueued; - } -#endif - } + if (status == mStatus_NoError) *e = extra; mDNS_Unlock(m); return(status); @@ -8874,21 +8867,11 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS // 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) +mDNSexport mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt) { // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() if (mDNSIPPortIsZero(sr->RR_SRV.resrec.rdata->u.srv.port)) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); -#if !defined(UNICAST_DISABLED) && USE_SEPARATE_UDNS_SERVICE_LIST - if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name))) - { - mStatus status; - mDNS_Lock(m); - status = uDNS_DeregisterService(m, sr); - mDNS_Unlock(m); - return(status); - } -#endif if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) { debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c); @@ -8896,7 +8879,7 @@ mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) } else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) { - debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c); + LogInfo("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c); // Avoid race condition: // If a service gets a conflict, then we set the Conflict flag to tell us to generate // an mStatus_NameConflict message when we get the mStatus_MemFree for our PTR record. @@ -8924,7 +8907,7 @@ mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) 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); + mDNS_Deregister_internal(m, &sr->RR_ADV, drt); // We deregister all of the extra records, but we leave the sr->Extras list intact // in case the client wants to do a RenameAndReregister and reinstate the registration @@ -8935,14 +8918,9 @@ mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) } for (i=0; i<sr->NumSubTypes; i++) - mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal); - - // Be sure to deregister the PTR last! - // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback, - // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback, - // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure - // we've deregistered all our records and done any other necessary cleanup before that happens. - status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal); + mDNS_Deregister_internal(m, &sr->SubTypes[i], drt); + + status = mDNS_Deregister_internal(m, &sr->RR_PTR, drt); mDNS_Unlock(m); return(status); } @@ -8978,20 +8956,33 @@ mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, return(mDNS_Register(m, rr)); } +mDNSlocal mDNSBool mDNS_IdUsedInResourceRecordsList(mDNS * const m, mDNSOpaque16 id) + { + AuthRecord *r; + for (r = m->ResourceRecords; r; r=r->next) if (mDNSSameOpaque16(id, r->updateid)) return mDNStrue; + return mDNSfalse; + } + +mDNSlocal mDNSBool mDNS_IdUsedInQuestionsList(mDNS * const m, mDNSOpaque16 id) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) if (mDNSSameOpaque16(id, q->TargetQID)) return mDNStrue; + return mDNSfalse; + } + mDNSexport mDNSOpaque16 mDNS_NewMessageID(mDNS * const m) { mDNSOpaque16 id; int i; + for (i=0; i<10; i++) { - AuthRecord *r; - DNSQuestion *q; - id = mDNSOpaque16fromIntVal(1 + mDNSRandom(0xFFFE)); - for (r = m->ResourceRecords; r; r=r->next) if (mDNSSameOpaque16(id, r->updateid )) continue; - for (q = m->Questions; q; q=q->next) if (mDNSSameOpaque16(id, q->TargetQID)) continue; - break; + id = mDNSOpaque16fromIntVal(1 + (mDNSu16)mDNSRandom(0xFFFE)); + if (!mDNS_IdUsedInResourceRecordsList(m, id) && !mDNS_IdUsedInQuestionsList(m, id)) break; } + debugf("mDNS_NewMessageID: %5d", mDNSVal16(id)); + return id; } @@ -9001,202 +8992,363 @@ mDNSexport mDNSOpaque16 mDNS_NewMessageID(mDNS * const m) #pragma mark - Sleep Proxy Server #endif -mDNSlocal void RestartProbing(mDNS *const m, AuthRecord *const rr) - { - // We reset ProbeCount, so we'll suppress our own answers for a while, to avoid generating ARP conflicts with a waking machine. - // If the machine does wake properly then we'll discard our records when we see the first new mDNS probe from that machine. - // If it does not wake (perhaps we just picked up a stray delayed packet sent before it went to sleep) - // then we'll transition out of probing state and start answering ARPs again. +mDNSlocal void RestartARPProbing(mDNS *const m, AuthRecord *const rr) + { + // If we see an ARP from a machine we think is sleeping, then either + // (i) the machine has woken, or + // (ii) it's just a stray old packet from before the machine slept + // To handle the second case, we reset ProbeCount, so we'll suppress our own answers for a while, to avoid + // generating ARP conflicts with a waking machine, and set rr->LastAPTime so we'll start probing again in 10 seconds. + // If the machine has just woken then we'll discard our records when we see the first new mDNS probe from that machine. + // If it was a stray old packet, then after 10 seconds we'll probe again and then start answering ARPs again. In this case we *do* + // need to send new ARP Announcements, because the owner's ARP broadcasts will have updated neighboring ARP caches, so we need to + // re-assert our (temporary) ownership of that IP address in order to receive subsequent packets addressed to that IPv4 address. + rr->resrec.RecordType = kDNSRecordTypeUnique; rr->ProbeCount = DefaultProbeCountForTypeUnique; - rr->AnnounceCount = InitialAnnounceCount; - InitializeLastAPTime(m, rr); + + // If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably + // still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding. + // If we *have* started announcing, the machine is probably in the process of waking back up, so in that case + // we're more cautious and we wait ten seconds before probing it again. We do this because while waking from + // sleep, some network interfaces tend to lose or delay inbound packets, and without this delay, if the waking machine + // didn't answer our three probes within three seconds then we'd announce and cause it an unnecessary address conflict. + if (rr->AnnounceCount == InitialAnnounceCount && m->timenow - rr->LastAPTime >= 0) + InitializeLastAPTime(m, rr); + else + { + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = mDNSPlatformOneSecond; + rr->LastAPTime = m->timenow + mDNSPlatformOneSecond * 9; // Send first packet at rr->LastAPTime + rr->ThisAPInterval, i.e. 10 seconds from now + SetNextAnnounceProbeTime(m, rr); + } } -mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) +mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, const mDNSInterfaceID InterfaceID) { - static const mDNSOpaque16 Ethertype_IP = { { 0x08, 0x00 } }; - static const mDNSOpaque32 ARP_EthIP_h0 = { { 0x08, 0x06, 0x00, 0x01 } }; // Ethertype (ARP = 0x0806), Hardware address space (Ethernet = 1) - static const mDNSOpaque32 ARP_EthIP_h1 = { { 0x08, 0x00, 0x06, 0x04 } }; // Protocol address space (IP = 0x0800), hlen, plen static const mDNSOpaque16 ARP_op_request = { { 0, 1 } }; - const EthernetHeader *const eth = (const EthernetHeader *)p; - const ARP_EthIP *const arp = (const ARP_EthIP *)(eth+1); - const IPv4Header *const v4 = (const IPv4Header *)(eth+1); - const IPv6Header *const v6 = (const IPv6Header *)(eth+1); - if (end >= p+42 && *(mDNSu32*)(p+12) == ARP_EthIP_h0.NotAnInteger && *(mDNSu32*)(p+16) == ARP_EthIP_h1.NotAnInteger) - { - AuthRecord *rr; - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - if (!intf) return; + AuthRecord *rr; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) return; - debugf("Got ARP from %.4a/%.6a for %.4a", &arp->spa, &arp->sha, &arp->tpa); + mDNS_Lock(m); - mDNS_Lock(m); + // Pass 1: + // Process ARP Requests and Probes (but not Announcements), and generate an ARP Reply if necessary. + // We also process ARPs from our own kernel (and 'answer' them by injecting a local ARP table entry) + // We ignore ARP Announcements here -- Announcements are not questions, they're assertions, so we don't need to answer them. + // The times we might need to react to an ARP Announcement are: + // (i) as an indication that the host in question has not gone to sleep yet (so we should delay beginning to proxy for it) or + // (ii) if it's a conflicting Announcement from another host + // -- and we check for these in Pass 2 below. + if (mDNSSameOpaque16(arp->op, ARP_op_request) && !mDNSSameIPv4Address(arp->spa, arp->tpa)) + { + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->tpa)) + { + static const char msg1[] = "ARP Req from owner -- re-probing"; + static const char msg2[] = "Ignoring ARP Request from "; + static const char msg3[] = "Creating Local ARP Cache entry "; + static const char msg4[] = "Answering ARP Request from "; + const char *const msg = mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC) ? msg1 : + (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : + mDNSSameEthAddress(&arp->sha, &intf->MAC) ? msg3 : msg4; + LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", + intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + if (msg == msg1) RestartARPProbing(m, rr); + else if (msg == msg3) mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + else if (msg == msg4) SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha); + } + } - // Pass 1: - // Process ARP Requests and Probes (but not Announcements), and generate an ARP Reply if necessary. - // We also process and answer ARPs from our own kernel (no special treatment for localhost). - // We ignore ARP Announcements here -- Announcements are not questions, they're assertions, so we don't need to answer them. - // The only time we might need to respond to an ARP Announcement is if it's a conflict -- and we check for that in Pass 2 below. - if (mDNSSameOpaque16(arp->op, ARP_op_request) && !mDNSSameIPv4Address(arp->spa, arp->tpa)) + // Pass 2: + // For all types of ARP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. + // (Strictly speaking we're only checking Announcement/Request/Reply packets, since ARP Probes have zero Sender IP address, + // so by definition (and by design) they can never conflict with any real (i.e. non-zero) IP address). + // We ignore ARPs we sent ourselves (Sender MAC address is our MAC address) because our own proxy ARPs do not constitute a conflict that we need to handle. + // If we see an apparently conflicting ARP, we check the sender hardware address: + // If the sender hardware address is the original owner this is benign, so we just suppress our own proxy answering for a while longer. + // If the sender hardware address is *not* the original owner, then this is a conflict, and we need to wake the sleeping machine to handle it. + if (mDNSSameEthAddress(&arp->sha, &intf->MAC)) + debugf("ARP from self for %.4a", &arp->tpa); + else + { + if (!mDNSSameIPv4Address(arp->spa, zerov4Addr)) for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->tpa)) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa)) { - static const char msg1[] = "ARP Req from owner -- re-probing"; - static const char msg2[] = "Ignoring ARP Request from "; - static const char msg3[] = "Creating Local ARP Cache entry "; - static const char msg4[] = "Answering ARP Request from "; - const char *const msg = mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC) ? msg1 : - (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : - mDNSSameEthAddress(&arp->sha, &intf->MAC) ? msg3 : msg4; - LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", - InterfaceNameForID(m, InterfaceID), msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - if (msg == msg1) RestartProbing(m, rr); - else if (msg == msg3) mDNSPlatformSetLocalARP(&arp->tpa, &rr->WakeUp.IMAC, InterfaceID); - else if (msg == msg4) SendARP(m, 2, rr, arp->tpa.b, arp->sha.b, arp->spa.b, arp->sha.b); - } - - // Pass 2: - // For all types of ARP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. - // (Strictly speaking we're only checking Announcement/Request/Reply packets, since ARP Probes have zero Sender IP address, - // so by definition (and by design) they can never conflict with any real (i.e. non-zero) IP address). - // We ignore ARPs we sent ourselves (Sender MAC address is our MAC address) because our own proxy ARPs do not constitute a conflict that we need to handle. - // If we see an apparently conflicting ARP, we check the sender hardware address: - // If the sender hardware address is the original owner this is benign, so we just suppress our own proxy answering for a while longer. - // If the sender hardware address is *not* the original owner, then this is a conflict, and we need to wake the sleeping machine to handle it. - if (mDNSSameEthAddress(&arp->sha, &intf->MAC)) - debugf("ARP from self for %.4a", &arp->tpa); - else - { - if (!mDNSSameIPv4Address(arp->spa, zerov4Addr)) - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa)) + RestartARPProbing(m, rr); + if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) + LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname, + mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", + &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); + else { - RestartProbing(m, rr); - if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) - LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", - InterfaceNameForID(m, InterfaceID), - mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement" : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", - &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); - else - { - LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", - InterfaceNameForID(m, InterfaceID), &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); - } + LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, + &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); } - } - - mDNS_Unlock(m); + } } - else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IP) && (v4->flagsfrags.b[0] & 0x1F) == 0 && v4->flagsfrags.b[1] == 0) + + mDNS_Unlock(m); + } + +/* +// Option 1 is Source Link Layer Address Option +// Option 2 is Target Link Layer Address Option +mDNSlocal const mDNSEthAddr *GetLinkLayerAddressOption(const IPv6NDP *const ndp, const mDNSu8 *const end, mDNSu8 op) + { + const mDNSu8 *options = (mDNSu8 *)(ndp+1); + while (options < end) { - const mDNSu8 *const trans = p + 14 + (v4->vlen & 0xF) * 4; - const mDNSu8 *const required = trans + (v4->protocol == 1 ? 4 : v4->protocol == 6 ? 20 : v4->protocol == 17 ? 8 : 0); - debugf("Got IPv4 from %.4a to %.4a", &v4->src, &v4->dst); - if (end >= required) - { - #define SSH_AsNumber 22 - #define ARD_AsNumber 3283 - #define IPSEC_AsNumber 4500 - static const mDNSIPPort SSH = { { SSH_AsNumber >> 8, SSH_AsNumber & 0xFF } }; - static const mDNSIPPort ARD = { { ARD_AsNumber >> 8, ARD_AsNumber & 0xFF } }; - static const mDNSIPPort IPSEC = { { IPSEC_AsNumber >> 8, IPSEC_AsNumber & 0xFF } }; + debugf("NDP Option %02X len %2d %d", options[0], options[1], end - options); + if (options[0] == op && options[1] == 1) return (const mDNSEthAddr*)(options+2); + options += options[1] * 8; + } + return mDNSNULL; + } +*/ - mDNSBool wake = mDNSfalse; - mDNSIPPort port = zeroIPPort; - - switch (v4->protocol) +mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, const mDNSv6Addr *spa, + const IPv6NDP *const ndp, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) + { + AuthRecord *rr; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + if (!intf) return; + + mDNS_Lock(m); + + // Pass 1: Process Neighbor Solicitations, and generate a Neighbor Advertisement if necessary. + if (ndp->type == NDP_Sol) + { + //const mDNSEthAddr *const sha = GetLinkLayerAddressOption(ndp, end, NDP_SrcLL); + (void)end; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, ndp->target)) { - #define XX wake ? "Received" : "Ignoring", end-p - case 1: LogSPS("%s %d-byte ICMP from %.4a to %.4a", XX, &v4->src, &v4->dst); - break; - - case 6: { - const TCPHeader *const tcp = (const TCPHeader *)trans; - port = tcp->dst; - - // Plan to wake if - // (a) RST is not set, AND - // (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone. - wake = (!(tcp->flags & 4) && (tcp->flags & 3) != 1); - - // For now, to reduce spurious wakeups, we wake only for TCP SYN, - // except for ssh connections, where we'll wake for plain data packets too - if (!mDNSSameIPPort(port, SSH) && !(tcp->flags & 2)) wake = mDNSfalse; - - LogSPS("%s %d-byte TCP from %.4a:%d to %.4a:%d%s%s%s", XX, - &v4->src, mDNSVal16(tcp->src), &v4->dst, mDNSVal16(port), - (tcp->flags & 2) ? " SYN" : "", - (tcp->flags & 1) ? " FIN" : "", - (tcp->flags & 4) ? " RST" : ""); - } - break; + static const char msg1[] = "NDP Req from owner -- re-probing"; + static const char msg2[] = "Ignoring NDP Request from "; + static const char msg3[] = "Creating Local NDP Cache entry "; + static const char msg4[] = "Answering NDP Request from "; + static const char msg5[] = "Answering NDP Probe from "; + const char *const msg = sha && mDNSSameEthAddress(sha, &rr->WakeUp.IMAC) ? msg1 : + (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : + sha && mDNSSameEthAddress(sha, &intf->MAC) ? msg3 : + spa && mDNSIPv6AddressIsZero(*spa) ? msg4 : msg5; + LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s", + intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + if (msg == msg1) RestartARPProbing(m, rr); + else if (msg == msg3) + { + if (!(m->KnownBugs & mDNS_KnownBug_LimitedIPv6)) + mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + } + else if (msg == msg4) SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha ); + else if (msg == msg5) SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + } + } + + // Pass 2: For all types of NDP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding. + if (mDNSSameEthAddress(sha, &intf->MAC)) + debugf("NDP from self for %.16a", &ndp->target); + else + { + // For Neighbor Advertisements we check the Target address field, not the actual IPv6 source address. + // When a machine has both link-local and routable IPv6 addresses, it may send NDP packets making assertions + // about its routable IPv6 address, using its link-local address as the source address for all NDP packets. + // Hence it is the NDP target address we care about, not the actual packet source address. + if (ndp->type == NDP_Adv) spa = &ndp->target; + if (!mDNSSameIPv6Address(*spa, zerov6Addr)) + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa)) + { + RestartARPProbing(m, rr); + if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC)) + LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname, + ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr)); + else + { + LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, + sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } + } + } - case 17: { - const UDPHeader *const udp = (const UDPHeader *)trans; - mDNSu16 len = (mDNSu16)((mDNSu16)trans[4] << 8 | trans[5]); - port = udp->dst; - wake = mDNStrue; + mDNS_Unlock(m); + } - // For Back to My Mac UDP port 4500 (IPSEC) packets, we specially ignore NAT keepalive packets - if (mDNSSameIPPort(port, IPSEC)) wake = (len != 9 || end < trans + 9 || trans[8] != 0xFF); +mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAddr *const sha, const mDNSAddr *const src, const mDNSAddr *const dst, const mDNSu8 protocol, + const mDNSu8 *const p, const TransportLayerPacket *const t, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID, const mDNSu16 len) + { + const mDNSIPPort port = (protocol == 0x06) ? t->tcp.dst : (protocol == 0x11) ? t->udp.dst : zeroIPPort; + mDNSBool wake = mDNSfalse; - // For now, because we haven't yet worked out a clean elegant way to do this, we just special-case the - // Apple Remote Desktop port number -- we ignore all packets to UDP 3283 (the "Net Assistant" port), - // except for Apple Remote Desktop's explicit manual wakeup packet, which looks like this: - // UDP header (8 bytes) 13 88 00 6a 41 4e 41 20 (8 bytes) ffffffffffff (6 bytes) 16xMAC (96 bytes) = 118 bytes total - if (mDNSSameIPPort(port, ARD)) wake = (len >= 118 && end >= trans+10 && trans[8] == 0x13 && trans[9] == 0x88); + switch (protocol) + { + #define XX wake ? "Received" : "Ignoring", end-p + case 0x01: LogSPS("Ignoring %d-byte ICMP from %#a to %#a", end-p, src, dst); + break; - LogSPS("%s %d-byte UDP from %.4a:%d to %.4a:%d", XX, &v4->src, mDNSVal16(udp->src), &v4->dst, mDNSVal16(port)); - } - break; + case 0x06: { + #define SSH_AsNumber 22 + static const mDNSIPPort SSH = { { SSH_AsNumber >> 8, SSH_AsNumber & 0xFF } }; - default: LogSPS("%s %d-byte IP packet unknown protocol %d from %.4a to %.4a", XX, v4->protocol, &v4->src, &v4->dst); - break; - } - - if (wake) - { - AuthRecord *rr, *r2; + // Plan to wake if + // (a) RST is not set, AND + // (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone. + wake = (!(t->tcp.flags & 4) && (t->tcp.flags & 3) != 1); - mDNS_Lock(m); - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == InterfaceID && - rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, v4->dst)) + // For now, to reduce spurious wakeups, we wake only for TCP SYN, + // except for ssh connections, where we'll wake for plain data packets too + if (!mDNSSameIPPort(port, SSH) && !(t->tcp.flags & 2)) wake = mDNSfalse; + + LogSPS("%s %d-byte TCP from %#a:%d to %#a:%d%s%s%s", XX, + src, mDNSVal16(t->tcp.src), dst, mDNSVal16(port), + (t->tcp.flags & 2) ? " SYN" : "", + (t->tcp.flags & 1) ? " FIN" : "", + (t->tcp.flags & 4) ? " RST" : ""); + } + break; + + case 0x11: { + #define ARD_AsNumber 3283 + static const mDNSIPPort ARD = { { ARD_AsNumber >> 8, ARD_AsNumber & 0xFF } }; + const mDNSu16 udplen = (mDNSu16)((mDNSu16)t->bytes[4] << 8 | t->bytes[5]); // Length *including* 8-byte UDP header + if (udplen >= sizeof(UDPHeader)) { - const mDNSu8 *const tp = (v4->protocol == 6) ? (mDNSu8 *)"\x4_tcp" : (mDNSu8 *)"\x4_udp"; - for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (r2->resrec.InterfaceID == InterfaceID && mDNSSameEthAddress(&r2->WakeUp.HMAC, &rr->WakeUp.HMAC) && - r2->resrec.rrtype == kDNSType_SRV && mDNSSameIPPort(r2->resrec.rdata->u.srv.port, port) && - SameDomainLabel(SkipLeadingLabels(r2->resrec.name, 2)->c, tp)) - break; - if (!r2 && mDNSSameIPPort(port, IPSEC)) r2 = rr; // So that we wake for BTMM IPSEC packets, even without a matching SRV record - if (r2) + const mDNSu16 datalen = udplen - sizeof(UDPHeader); + wake = mDNStrue; + + // For Back to My Mac UDP port 4500 (IPSEC) packets, we do some special handling + if (mDNSSameIPPort(port, IPSECPort)) { - rr->AnnounceCount = 0; - LogMsg("Waking host at %s %.4a H-MAC %.6a I-MAC %.6a for %s", - InterfaceNameForID(m, rr->resrec.InterfaceID), &v4->dst, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, r2)); - SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); + // Specifically ignore NAT keepalive packets + if (datalen == 1 && end >= &t->bytes[9] && t->bytes[8] == 0xFF) wake = mDNSfalse; + else + { + // Skip over the Non-ESP Marker if present + const mDNSBool NonESP = (end >= &t->bytes[12] && t->bytes[8] == 0 && t->bytes[9] == 0 && t->bytes[10] == 0 && t->bytes[11] == 0); + const IKEHeader *const ike = (IKEHeader *)(t + (NonESP ? 12 : 8)); + const mDNSu16 ikelen = datalen - (NonESP ? 4 : 0); + if (ikelen >= sizeof(IKEHeader) && end >= ((mDNSu8 *)ike) + sizeof(IKEHeader)) + if ((ike->Version & 0x10) == 0x10) + { + // ExchangeType == 5 means 'Informational' <http://www.ietf.org/rfc/rfc2408.txt> + // ExchangeType == 34 means 'IKE_SA_INIT' <http://www.iana.org/assignments/ikev2-parameters> + if (ike->ExchangeType == 5 || ike->ExchangeType == 34) wake = mDNSfalse; + LogSPS("%s %d-byte IKE ExchangeType %d", XX, ike->ExchangeType); + } + } } - else - LogSPS("Sleeping host at %s %.4a %.6a has no service on %#s %d", - InterfaceNameForID(m, rr->resrec.InterfaceID), &v4->dst, &rr->WakeUp.HMAC, tp, mDNSVal16(port)); + + // For now, because we haven't yet worked out a clean elegant way to do this, we just special-case the + // Apple Remote Desktop port number -- we ignore all packets to UDP 3283 (the "Net Assistant" port), + // except for Apple Remote Desktop's explicit manual wakeup packet, which looks like this: + // UDP header (8 bytes) + // Payload: 13 88 00 6a 41 4e 41 20 (8 bytes) ffffffffffff (6 bytes) 16xMAC (96 bytes) = 110 bytes total + if (mDNSSameIPPort(port, ARD)) wake = (datalen >= 110 && end >= &t->bytes[10] && t->bytes[8] == 0x13 && t->bytes[9] == 0x88); + + LogSPS("%s %d-byte UDP from %#a:%d to %#a:%d", XX, src, mDNSVal16(t->udp.src), dst, mDNSVal16(port)); } - mDNS_Unlock(m); - } - } + } + break; + + case 0x3A: if (&t->bytes[len] <= end) + { + mDNSu16 checksum = IPv6CheckSum(&src->ip.v6, &dst->ip.v6, protocol, t->bytes, len); + if (!checksum) mDNSCoreReceiveRawND(m, sha, &src->ip.v6, &t->ndp, &t->bytes[len], InterfaceID); + else LogInfo("IPv6CheckSum bad %04X %02X%02X from %#a to %#a", checksum, t->bytes[2], t->bytes[3], src, dst); + } + break; + + default: LogSPS("Ignoring %d-byte IP packet unknown protocol %d from %#a to %#a", end-p, protocol, src, dst); + break; } - else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IP) && (v4->flagsfrags.b[0] & 0x1F) == 0 && v4->flagsfrags.b[1] == 0) + + if (wake) { - debugf("Got IPv6 from %.16a to %.16a", &v4->src, &v6->dst); - (void)v6; + AuthRecord *rr, *r2; + + mDNS_Lock(m); + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && + rr->resrec.RecordType != kDNSRecordTypeDeregistering && + rr->AddressProxy.type && mDNSSameAddress(&rr->AddressProxy, dst)) + { + const mDNSu8 *const tp = (protocol == 6) ? (const mDNSu8 *)"\x4_tcp" : (const mDNSu8 *)"\x4_udp"; + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (r2->resrec.InterfaceID == InterfaceID && mDNSSameEthAddress(&r2->WakeUp.HMAC, &rr->WakeUp.HMAC) && + r2->resrec.RecordType != kDNSRecordTypeDeregistering && + r2->resrec.rrtype == kDNSType_SRV && mDNSSameIPPort(r2->resrec.rdata->u.srv.port, port) && + SameDomainLabel(ThirdLabel(r2->resrec.name)->c, tp)) + break; + if (!r2 && mDNSSameIPPort(port, IPSECPort)) r2 = rr; // So that we wake for BTMM IPSEC packets, even without a matching SRV record + if (r2) + { + LogMsg("Waking host at %s %#a H-MAC %.6a I-MAC %.6a for %s", + InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, r2)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } + else + LogSPS("Sleeping host at %s %#a %.6a has no service on %#s %d", + InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, tp, mDNSVal16(port)); + } + mDNS_Unlock(m); + } + } + +mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) + { + static const mDNSOpaque16 Ethertype_ARP = { { 0x08, 0x06 } }; // Ethertype 0x0806 = ARP + static const mDNSOpaque16 Ethertype_IPv4 = { { 0x08, 0x00 } }; // Ethertype 0x0800 = IPv4 + static const mDNSOpaque16 Ethertype_IPv6 = { { 0x86, 0xDD } }; // Ethertype 0x86DD = IPv6 + static const mDNSOpaque16 ARP_hrd_eth = { { 0x00, 0x01 } }; // Hardware address space (Ethernet = 1) + static const mDNSOpaque16 ARP_pro_ip = { { 0x08, 0x00 } }; // Protocol address space (IP = 0x0800) + + // Note: BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header. + // In other words, we can safely assume that pkt below (ARP, IPv4 or IPv6) is properly word aligned, + // but if pkt is 4-byte aligned, that necessarily means that eth CANNOT also be 4-byte aligned + // since it points to a an address 14 bytes before pkt. + const EthernetHeader *const eth = (const EthernetHeader *)p; + const NetworkLayerPacket *const pkt = (const NetworkLayerPacket *)(eth+1); + mDNSAddr src, dst; + #define RequiredCapLen(P) ((P)==0x01 ? 4 : (P)==0x06 ? 20 : (P)==0x11 ? 8 : (P)==0x3A ? 24 : 0) + + // Is ARP? Length must be at least 14 + 28 = 42 bytes + if (end >= p+42 && mDNSSameOpaque16(eth->ethertype, Ethertype_ARP) && mDNSSameOpaque16(pkt->arp.hrd, ARP_hrd_eth) && mDNSSameOpaque16(pkt->arp.pro, ARP_pro_ip)) + mDNSCoreReceiveRawARP(m, &pkt->arp, InterfaceID); + // Is IPv4 with zero fragmentation offset? Length must be at least 14 + 20 = 34 bytes + else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv4) && (pkt->v4.flagsfrags.b[0] & 0x1F) == 0 && pkt->v4.flagsfrags.b[1] == 0) + { + const mDNSu8 *const trans = p + 14 + (pkt->v4.vlen & 0xF) * 4; + debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src, &pkt->v4.dst); + src.type = mDNSAddrType_IPv4; src.ip.v4 = pkt->v4.src; + dst.type = mDNSAddrType_IPv4; dst.ip.v4 = pkt->v4.dst; + if (end >= trans + RequiredCapLen(pkt->v4.protocol)) + mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, end, InterfaceID, 0); + } + // Is IPv6? Length must be at least 14 + 28 = 42 bytes + else if (end >= p+54 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv6)) + { + const mDNSu8 *const trans = p + 54; + debugf("Got IPv6 %02X from %.16a to %.16a", pkt->v6.pro, &pkt->v6.src, &pkt->v6.dst); + src.type = mDNSAddrType_IPv6; src.ip.v6 = pkt->v6.src; + dst.type = mDNSAddrType_IPv6; dst.ip.v6 = pkt->v6.dst; + if (end >= trans + RequiredCapLen(pkt->v6.pro)) + mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v6.pro, p, (TransportLayerPacket*)trans, end, InterfaceID, + (mDNSu16)pkt->bytes[4] << 8 | pkt->bytes[5]); } } mDNSlocal void ConstructSleepProxyServerName(mDNS *const m, domainlabel *name) { - name->c[0] = mDNS_snprintf((char*)name->c+1, 62, "%d-%d-%d-%d %#s", + name->c[0] = (mDNSu8)mDNS_snprintf((char*)name->c+1, 62, "%d-%d-%d-%d %#s", m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, &m->nicelabel); } @@ -9210,7 +9362,7 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s m->SPSState = 3; else { - m->SPSState = (m->SPSSocket != mDNSNULL); + m->SPSState = (mDNSu8)(m->SPSSocket != mDNSNULL); if (m->SPSState) { domainlabel name; @@ -9228,15 +9380,19 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s } } -mDNSexport void mDNSCoreBeSleepProxyServer(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower) +// Called with lock held +mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower) { + // This routine uses mDNS_DeregisterService and calls SleepProxyServerCallback, so we execute in user callback context + mDNS_DropLockBeforeCallback(); + // If turning off SPS, close our socket // (Do this first, BEFORE calling mDNS_DeregisterService below) if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; } // If turning off, or changing type, deregister old name if (m->SPSState == 1 && sps != m->SPSType) - { m->SPSState = 2; mDNS_DeregisterService(m, &m->SPSRecords); } + { m->SPSState = 2; mDNS_DeregisterService_drt(m, &m->SPSRecords, sps ? mDNS_Dereg_rapid : mDNS_Dereg_normal); } // Record our new SPS parameters m->SPSType = sps; @@ -9250,10 +9406,17 @@ mDNSexport void mDNSCoreBeSleepProxyServer(mDNS *const m, mDNSu8 sps, mDNSu8 por if (!m->SPSSocket) { m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort); - if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); return; } + if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; } } if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree); } + else if (m->SPSState) + { + LogSPS("mDNSCoreBeSleepProxyServer turning off from state %d; will wake clients", m->SPSState); + m->NextScheduledSPS = m->timenow; + } +fail: + mDNS_ReclaimLockAfterCallback(); } // *************************************************************************** @@ -9332,9 +9495,12 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->RandomQueryDelay = 0; m->RandomReconfirmDelay = 0; m->PktNum = 0; + m->LocalRemoveEvents = mDNSfalse; m->SleepState = SleepState_Awake; m->SleepSeqNum = 0; m->SystemWakeOnLANEnabled = mDNSfalse; + m->SentSleepProxyRegistration = mDNSfalse; + m->AnnounceOwner = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond); m->DelaySleep = 0; m->SleepLimit = 0; @@ -9350,7 +9516,11 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->rrcache_report = 10; m->rrcache_free = mDNSNULL; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + m->rrcache_hash[slot] = mDNSNULL; + m->rrcache_nextcheck[slot] = timenow + 0x78000000;; + } mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize); @@ -9372,9 +9542,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, #ifndef UNICAST_DISABLED m->NextuDNSEvent = timenow + 0x78000000; m->NextSRVUpdate = timenow + 0x78000000; - m->SuppressStdPort53Queries = 0; - m->ServiceRegistrations = mDNSNULL; m->DNSServers = mDNSNULL; m->Router = zeroAddr; @@ -9392,6 +9560,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->AutoTunnelLabel.c[0] = 0; m->RegisterSearchDomains = mDNSfalse; + m->RegisterAutoTunnel6 = mDNStrue; // NAT traversal fields m->NATTraversals = mDNSNULL; @@ -9429,6 +9598,15 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, #if APPLE_OSX_mDNSResponder m->TunnelClients = mDNSNULL; + +#if ! NO_WCF + CHECK_WCF_FUNCTION(WCFConnectionNew) + { + m->WCF = WCFConnectionNew(); + if (!m->WCF) { LogMsg("WCFConnectionNew failed"); return -1; } + } +#endif + #endif result = mDNSPlatformInit(m); @@ -9456,7 +9634,7 @@ mDNSexport void mDNS_ConfigChanged(mDNS *const m) // When SleepProxyServerCallback gets the mStatus_MemFree message, // it will reregister the service under the new name m->SPSState = 2; - mDNS_DeregisterService(m, &m->SPSRecords); + mDNS_DeregisterService_drt(m, &m->SPSRecords, mDNS_Dereg_rapid); } } @@ -9480,10 +9658,186 @@ mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const (void) lameduck; (void) ptr; - debugf("uDNS_SetupDNSConfig: %s cache record due to %s server %p %#a:%d (%##s): %s", purge ? "purging" : "reconfirming", lameduck ? "lame duck" : "new", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, CRDisplayString(m, cr)); + debugf("PurgeOrReconfirmCacheRecord: %s cache record due to %s server %p %#a:%d (%##s): %s", + purge ? "purging" : "reconfirming", + lameduck ? "lame duck" : "new", + ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, CRDisplayString(m, cr)); - if (purge) mDNS_PurgeCacheResourceRecord(m, cr); - else mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + if (purge) + { + LogInfo("PurgeorReconfirmCacheRecord: Purging Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType); + mDNS_PurgeCacheResourceRecord(m, cr); + } + else + { + LogInfo("PurgeorReconfirmCacheRecord: Reconfirming Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType); + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); + } + } + +mDNSlocal void CacheRecordResetDNSServer(mDNS *const m, DNSQuestion *q, DNSServer *new) + { + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + mDNSBool found = mDNSfalse; + mDNSBool foundNew = mDNSfalse; + DNSServer *old = q->qDNSServer; + mDNSBool newQuestion = IsQuestionNew(m, q); + DNSQuestion *qptr; + + // This function is called when the DNSServer is updated to the new question. There may already be + // some cache entries matching the old DNSServer and/or new DNSServer. There are four cases. In the + // following table, "Yes" denotes that a cache entry was found for old/new DNSServer. + // + // old DNSServer new DNSServer + // + // Case 1 Yes Yes + // Case 2 No Yes + // Case 3 Yes No + // Case 4 No No + // + // Case 1: There are cache entries for both old and new DNSServer. We handle this case by simply + // expiring the old Cache entries, deliver a RMV event (if an ADD event was delivered before) + // followed by the ADD event of the cache entries corresponding to the new server. This + // case happens when we pick a DNSServer, issue a query and get a valid response and create + // cache entries after which it stops responding. Another query (non-duplicate) picks a different + // DNSServer and creates identical cache entries (perhaps through records in Additional records). + // Now if the first one expires and tries to pick the new DNSServer (the original DNSServer + // is not responding) we will find cache entries corresponding to both DNSServers. + // + // Case 2: There are no cache entries for the old DNSServer but there are some for the new DNSServer. + // This means we should deliver an ADD event. Normally ADD events are delivered by + // AnswerNewQuestion if it is a new question. So, we check to see if it is a new question + // and if so, leave it to AnswerNewQuestion to deliver it. Otherwise, we use + // AnswerQuestionsForDNSServerChanges to deliver the ADD event. This case happens when a + // question picks a DNS server for which AnswerNewQuestion could not deliver an answer even + // though there were potential cache entries but DNSServer did not match. Now when we + // pick a new DNSServer, those cache entries may answer this question. + // + // Case 3: There are the cache entries for the old DNSServer but none for the new. We just move + // the old cache entries to point to the new DNSServer and the caller is expected to + // do a purge or reconfirm to delete or validate the RDATA. We don't need to do anything + // special for delivering ADD events, as it should have been done/will be done by + // AnswerNewQuestion. This case happens when we picked a DNSServer, sent the query and + // got a response and the cache is expired now and we are reissuing the question but the + // original DNSServer does not respond. + // + // Case 4: There are no cache entries either for the old or for the new DNSServer. There is nothing + // much we can do here. + // + // Case 2 and 3 are the most common while case 4 is possible when no DNSServers are working. Case 1 + // is relatively less likely to happen in practice + + // Temporarily set the DNSServer to look for the matching records for the new DNSServer. + q->qDNSServer = new; + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (SameNameRecordAnswersQuestion(&rp->resrec, q)) + { + LogInfo("CacheRecordResetDNSServer: Found cache record %##s for new DNSServer address: %#a", rp->resrec.name->c, + (rp->resrec.rDNSServer != mDNSNULL ? &rp->resrec.rDNSServer->addr : mDNSNULL)); + foundNew = mDNStrue; + break; + } + } + q->qDNSServer = old; + + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (SameNameRecordAnswersQuestion(&rp->resrec, q)) + { + // Case1 + found = mDNStrue; + if (foundNew) + { + LogInfo("CacheRecordResetDNSServer: Flushing Resourcerecord %##s, before:%#a, after:%#a", rp->resrec.name->c, + (rp->resrec.rDNSServer != mDNSNULL ? &rp->resrec.rDNSServer->addr : mDNSNULL), + (new != mDNSNULL ? &new->addr : mDNSNULL)); + mDNS_PurgeCacheResourceRecord(m, rp); + if (newQuestion) + { + // "q" is not a duplicate question. If it is a newQuestion, then the CRActiveQuestion can't be + // possibly set as it is set only when we deliver the ADD event to the question. + if (rp->CRActiveQuestion != mDNSNULL) + { + LogMsg("CacheRecordResetDNSServer: ERROR!!: CRActiveQuestion %p set, current question %p, name %##s", rp->CRActiveQuestion, q, q->qname.c); + rp->CRActiveQuestion = mDNSNULL; + } + // if this is a new question, then we never delivered an ADD yet, so don't deliver the RMV. + continue; + } + } + LogInfo("CacheRecordResetDNSServer: resetting cache record %##s DNSServer address before:%#a," + " after:%#a, CRActiveQuestion %p", rp->resrec.name->c, (rp->resrec.rDNSServer != mDNSNULL ? + &rp->resrec.rDNSServer->addr : mDNSNULL), (new != mDNSNULL ? &new->addr : mDNSNULL), + rp->CRActiveQuestion); + // Though we set it to the new DNS server, the caller is *assumed* to do either a purge + // or reconfirm or send out questions to the "new" server to verify whether the cached + // RDATA is valid + rp->resrec.rDNSServer = new; + } + } + + // Case 1 and Case 2 + if ((found && foundNew) || (!found && foundNew)) + { + if (newQuestion) + LogInfo("CacheRecordResetDNSServer: deliverAddEvents not set for question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + else if (QuerySuppressed(q)) + LogInfo("CacheRecordResetDNSServer: deliverAddEvents not set for suppressed question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + else + { + LogInfo("CacheRecordResetDNSServer: deliverAddEvents set for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + q->deliverAddEvents = mDNStrue; + for (qptr = q->next; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) qptr->deliverAddEvents = mDNStrue; + } + return; + } + + // Case 3 and Case 4 + return; + } + +mDNSexport void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *new) + { + DNSQuestion *qptr; + + // 1. Whenever we change the DNS server, we change the message identifier also so that response + // from the old server is not accepted as a response from the new server but only messages + // from the new server are accepted as valid responses. We do it irrespective of whether "new" + // is NULL or not. It is possible that we send two queries, no responses, pick a new DNS server + // which is NULL and now the response comes back and will try to penalize the DNS server which + // is NULL. By setting the messageID here, we will not accept that as a valid response. + + q->TargetQID = mDNS_NewMessageID(m); + + // 2. Move the old cache records to point them at the new DNSServer so that we can deliver the ADD/RMV events + // appropriately. At any point in time, we want all the cache records point only to one DNSServer for a given + // question. "DNSServer" here is the DNSServer object and not the DNS server itself. It is possible to + // have the same DNS server address in two objects, one scoped and another not scoped. But, the cache is per + // DNSServer object. By maintaining the question and the cache entries point to the same DNSServer + // always, the cache maintenance and delivery of ADD/RMV events becomes simpler. + // + // CacheRecordResetDNSServer should be called only once for the non-duplicate question as once the cache + // entries are moved to point to the new DNSServer, we don't need to call it for the duplicate question + // and it is wrong to call for the duplicate question as it's decision to mark deliverAddevents will be + // incorrect. + + if (q->DuplicateOf) + LogMsg("DNSServerChangeForQuestion: ERROR: Called for duplicate question %##s", q->qname.c); + else + CacheRecordResetDNSServer(m, q, new); + + // 3. Make sure all the duplicate questions point to the same DNSServer so that delivery + // of events for all of them are consistent. Duplicates for a question are always inserted + // after in the list. + q->qDNSServer = new; + for (qptr = q->next ; qptr; qptr = qptr->next) + { + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = new; } + } } mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) @@ -9507,16 +9861,53 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // Let the platform layer get the current DNS information // The m->RegisterSearchDomains boolean is so that we lazily get the search domain list only on-demand // (no need to hit the network with domain enumeration queries until we actually need that information). - for (ptr = m->DNSServers; ptr; ptr = ptr->next) ptr->flags |= DNSServer_FlagDelete; + for (ptr = m->DNSServers; ptr; ptr = ptr->next) + { + ptr->penaltyTime = 0; + ptr->flags |= DNSServer_FlagDelete; + } mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL); + // Mark the records to be flushed that match a new resolver. We need to do this before + // we walk the questions below where we change the DNSServer pointer of the cache + // record + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.InterfaceID) continue; + + // We just mark them for purge or reconfirm. We can't affect the DNSServer pointer + // here as the code below that calls CacheRecordResetDNSServer relies on this + // + // The new DNSServer may be a scoped or non-scoped one. We use the active question's + // InterfaceID for looking up the right DNS server + ptr = GetServerForName(m, cr->resrec.name, cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL); + + // Purge or Reconfirm if this cache entry would use the new DNS server + if (ptr && (ptr != cr->resrec.rDNSServer)) + { + // As the DNSServers for this cache record is not the same anymore, we don't + // want any new questions to pick this old value + if (cr->CRActiveQuestion == mDNSNULL) + { + LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s", CRDisplayString(m, cr)); + mDNS_PurgeCacheResourceRecord(m, cr); + } + else + PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); + } + } // Update our qDNSServer pointers before we go and free the DNSServer object memory for (q = m->Questions; q; q=q->next) if (!mDNSOpaque16IsZero(q->TargetQID)) { - DNSServer *s = GetServerForName(m, &q->qname); - DNSServer *t = q->qDNSServer; + DNSServer *s, *t; + DNSQuestion *qptr; + if (q->DuplicateOf) continue; + SetValidDNSServers(m, q); + q->triedAllServersOnce = 0; + s = GetServerForQuestion(m, q); + t = q->qDNSServer; if (t != s) { // If DNS Server for this question has changed, reactivate it @@ -9524,20 +9915,47 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) t, t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"", s, s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"", q->qname.c, DNSTypeName(q->qtype)); - q->qDNSServer = s; + + // After we reset the DNSServer pointer on the cache records here, three things could happen: + // + // 1) The query gets sent out and when the actual response comes back later it is possible + // that the response has the same RDATA, in which case we update our cache entry. + // If the response is different, then the entry will expire and a new entry gets added. + // For the latter case to generate a RMV followed by ADD events, we need to reset the DNS + // server here to match the question and the cache record. + // + // 2) We might have marked the cache entries for purge above and for us to be able to generate the RMV + // events for the questions, the DNSServer on the question should match the Cache Record + // + // 3) We might have marked the cache entries for reconfirm above, for which we send the query out which is + // the same as the first case above. + + DNSServerChangeForQuestion(m, q, s); q->unansweredQueries = 0; - ActivateUnicastQuery(m, q, mDNStrue); + // We still need to pick a new DNSServer for the questions that have been + // suppressed, but it is wrong to activate the query as DNS server change + // could not possibly change the status of SuppressUnusable questions + if (!QuerySuppressed(q)) + { + debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + ActivateUnicastQuery(m, q, mDNStrue); + // ActivateUnicastQuery is called for duplicate questions also as it does something + // special for AutoTunnel questions + for (qptr = q->next ; qptr; qptr = qptr->next) + { + if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue); + } + } + } + else + { + debugf("uDNS_SetupDNSConfig: Not Updating DNS server question %p %##s (%s) DNS server %#a:%d %p %d", + q, q->qname.c, DNSTypeName(q->qtype), t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), q->DuplicateOf, q->SuppressUnusable); + for (qptr = q->next ; qptr; qptr = qptr->next) + if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } } } - // Flush all records that match a new resolver - FORALL_CACHERECORDS(slot, cg, cr) - { - ptr = GetServerForName(m, cr->resrec.name); - if (ptr && (ptr->flags & DNSServer_FlagNew) && !cr->resrec.InterfaceID) - PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); - } - while (*p) { if (((*p)->flags & DNSServer_FlagDelete) != 0) @@ -9546,13 +9964,58 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // We reconfirm any records that match, because in this world of split DNS, firewalls, etc. // different DNS servers can give different answers to the same question. ptr = *p; - ptr->flags &= ~DNSServer_FlagDelete; // Clear del so GetServerForName will (temporarily) find this server again before it's finally deleted FORALL_CACHERECORDS(slot, cg, cr) - if (!cr->resrec.InterfaceID && GetServerForName(m, cr->resrec.name) == ptr) + { + if (cr->resrec.InterfaceID) continue; + if (cr->resrec.rDNSServer == ptr) + { + // If we don't have an active question for this cache record, neither Purge can + // generate RMV events nor Reconfirm can send queries out. Just set the DNSServer + // pointer on the record NULL so that we don't point to freed memory (We might dereference + // DNSServer pointers from resource record for logging purposes). + // + // If there is an active question, point to its DNSServer as long as it does not point to the + // freed one. We already went through the questions above and made them point at either the + // new server or NULL if there is no server and also affected the cache entries that match + // this question. Hence, whenever we hit a resource record with a DNSServer that is just + // about to be deleted, we should never have an active question. The code below just tries to + // be careful logging messages if we ever hit this case. + + if (cr->CRActiveQuestion) + { + DNSQuestion *qptr = cr->CRActiveQuestion; + if (qptr->qDNSServer == mDNSNULL) + LogMsg("uDNS_SetupDNSConfig: Cache Record %s match: Active question %##s (%s) with DNSServer Address NULL, Server to be deleted %#a", + CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), &ptr->addr); + else + LogMsg("uDNS_SetupDNSConfig: Cache Record %s match: Active question %##s (%s) DNSServer Address %#a, Server to be deleted %#a", + CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), &qptr->qDNSServer->addr, &ptr->addr); + + if (qptr->qDNSServer == ptr) + { + qptr->validDNSServers = zeroOpaque64; + qptr->qDNSServer = mDNSNULL; + cr->resrec.rDNSServer = mDNSNULL; + } + else + { + cr->resrec.rDNSServer = qptr->qDNSServer; + } + } + else + { + LogInfo("uDNS_SetupDNSConfig: Cache Record %##s has no Active question, Record's DNSServer Address %#a, Server to be deleted %#a", + cr->resrec.name, &cr->resrec.rDNSServer->addr, &ptr->addr); + cr->resrec.rDNSServer = mDNSNULL; + } + PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue); + } + } *p = (*p)->next; debugf("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c); mDNSPlatformMemFree(ptr); + NumUnicastDNSServers--; } else { @@ -9571,12 +10034,11 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) FORALL_CACHERECORDS(slot, cg, cr) if (!cr->resrec.InterfaceID) { mDNS_PurgeCacheResourceRecord(m, cr); count++; } LogInfo("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache", m->DNSServers ? "DNS server became" : "No DNS servers", count); + + // Force anything that needs to get zone data to get that information again + RestartRecordGetZoneData(m); } - // If we no longer have any DNS servers, we need to force anything that needs to get zone data - // to get that information again (which will fail, since we have no more DNS servers) - if ((m->DNSServers == mDNSNULL) && (oldServers != mDNSNULL)) RestartRecordGetZoneData(m); - // Did our FQDN change? if (!SameDomainName(&fqdn, &m->FQDN)) { @@ -9610,6 +10072,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) if (m->FQDN.c[0]) mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); // Set status to 1 to indicate temporary failure } + debugf("uDNS_SetupDNSConfig: number of unicast DNS servers %d", NumUnicastDNSServers); return mStatus_NoError; } @@ -9626,21 +10089,24 @@ mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) } } -extern ServiceRecordSet *CurrentServiceRecordSet; - mDNSlocal void DeregLoop(mDNS *const m, AuthRecord *const start) { m->CurrentRecord = start; while (m->CurrentRecord) { AuthRecord *rr = m->CurrentRecord; + LogInfo("DeregLoop: %s deregistration for %p %02X %s", + (rr->resrec.RecordType != kDNSRecordTypeDeregistering) ? "Initiating " : "Accelerating", + rr, rr->resrec.RecordType, ARDisplayString(m, rr)); if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) + mDNS_Deregister_internal(m, rr, mDNS_Dereg_rapid); + else if (rr->AnnounceCount > 1) { - LogInfo("DeregLoop: Deregistering %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr)); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + rr->AnnounceCount = 1; + rr->LastAPTime = m->timenow - rr->ThisAPInterval; } - // Note: We mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because - // the list may have been changed in that call. + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now m->CurrentRecord = rr->next; } @@ -9653,17 +10119,25 @@ mDNSexport void mDNS_StartExit(mDNS *const m) mDNS_Lock(m); + LogInfo("mDNS_StartExit"); m->ShutdownTime = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); - mDNS_DropLockBeforeCallback(); // mDNSCoreBeSleepProxyServer expects to be called without the lock held, so we emulate that here - mDNSCoreBeSleepProxyServer(m, 0, 0, 0, 0); - mDNS_ReclaimLockAfterCallback(); + mDNSCoreBeSleepProxyServer_internal(m, 0, 0, 0, 0); + +#if APPLE_OSX_mDNSResponder +#if ! NO_WCF + CHECK_WCF_FUNCTION(WCFConnectionDealloc) + { + if (m->WCF) WCFConnectionDealloc((WCFConnection *)m->WCF); + } +#endif +#endif #ifndef UNICAST_DISABLED { SearchListElem *s; SuspendLLQs(m); - // Don't need to do SleepRecordRegistrations() or SleepServiceRegistrations() here, + // Don't need to do SleepRecordRegistrations() here // because we deregister all records and services later in this routine while (m->Hostnames) mDNS_RemoveDynDNSHostName(m, &m->Hostnames->fqdn); @@ -9694,9 +10168,11 @@ mDNSexport void mDNS_StartExit(mDNS *const m) // This has particularly important implications for our AutoTunnel records -- // when we deregister our AutoTunnel records below, we don't want their mStatus_MemFree // handlers to just turn around and attempt to re-register those same records. - // Clearing t->ExternalPort will cause the mStatus_MemFree callback handlers to not do this. + // Clearing t->ExternalPort/t->RequestedPort will cause the mStatus_MemFree callback handlers + // to not do this. t->ExternalAddress = zerov4Addr; t->ExternalPort = zeroIPPort; + t->RequestedPort = zeroIPPort; t->Lifetime = 0; t->Result = mStatus_NoError; } @@ -9722,24 +10198,9 @@ mDNSexport void mDNS_StartExit(mDNS *const m) m->SuppressSending = 0; } -#if !defined(UNICAST_DISABLED) && USE_SEPARATE_UDNS_SERVICE_LIST - CurrentServiceRecordSet = m->ServiceRegistrations; - while (CurrentServiceRecordSet) - { - ServiceRecordSet *srs = CurrentServiceRecordSet; - LogInfo("mDNS_StartExit: Deregistering uDNS service %##s", srs->RR_SRV.resrec.name->c); - uDNS_DeregisterService(m, srs); - if (CurrentServiceRecordSet == srs) - CurrentServiceRecordSet = srs->uDNS_next; - } -#endif - if (m->ResourceRecords) LogInfo("mDNS_StartExit: Sending final record deregistrations"); else LogInfo("mDNS_StartExit: No deregistering records remain"); - if (m->ServiceRegistrations) LogInfo("mDNS_StartExit: Sending final uDNS service deregistrations"); - else LogInfo("mDNS_StartExit: No deregistering uDNS services remain"); - for (rr = m->DuplicateRecords; rr; rr = rr->next) LogMsg("mDNS_StartExit: Should not still have Duplicate Records remaining: %02X %s", rr->resrec.RecordType, ARDisplayString(m, rr)); @@ -9757,7 +10218,6 @@ mDNSexport void mDNS_FinalExit(mDNS *const m) mDNSu32 rrcache_totalused = 0; mDNSu32 slot; AuthRecord *rr; - ServiceRecordSet *srs; LogInfo("mDNS_FinalExit: mDNSPlatformClose"); mDNSPlatformClose(m); @@ -9786,8 +10246,5 @@ mDNSexport void mDNS_FinalExit(mDNS *const m) for (rr = m->ResourceRecords; rr; rr = rr->next) LogMsg("mDNS_FinalExit failed to send goodbye for: %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr)); - for (srs = m->ServiceRegistrations; srs; srs = srs->uDNS_next) - LogMsg("mDNS_FinalExit failed to deregister service: %p %##s", srs, srs->RR_SRV.resrec.name->c); - LogInfo("mDNS_FinalExit: done"); } diff --git a/external/apache2/mDNSResponder/dist/mDNSCore/mDNSEmbeddedAPI.h b/external/apache2/mDNSResponder/dist/mDNSCore/mDNSEmbeddedAPI.h index 6412ca7e691..e08015e8c53 100755 --- a/external/apache2/mDNSResponder/dist/mDNSCore/mDNSEmbeddedAPI.h +++ b/external/apache2/mDNSResponder/dist/mDNSCore/mDNSEmbeddedAPI.h @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. - NOTE: If you're building an application that uses DNS Service Discovery this is probably NOT the header file you're looking for. @@ -50,934 +49,7 @@ you can still use the exact same client C code as you'd use on a general-purpose desktop system. - - Change History (most recent first): - -Log: mDNSEmbeddedAPI.h,v $ -Revision 1.573 2009/06/30 18:17:45 herscher -Add to 64 bit macro check for 64 bit Windows OSes - -Revision 1.572 2009/06/27 00:52:27 cheshire -<rdar://problem/6959273> mDNSResponder taking up 13% CPU with 400 KBps incoming bonjour requests -Removed overly-complicate and ineffective multi-packet known-answer snooping code -(Bracketed it with "#if ENABLE_MULTI_PACKET_QUERY_SNOOPING" for now; will delete actual code later) - -Revision 1.571 2009/06/26 01:55:54 cheshire -<rdar://problem/6890712> mDNS: iChat's Buddy photo always appears as the "shadow person" over Bonjour -Additional refinements -- except for the case of explicit queries for record types we don't have (for names we own), -add additional NSEC records only when there's space to do that without having to generate an additional packet - -Revision 1.570 2009/06/24 22:14:21 cheshire -<rdar://problem/6911445> Plugging and unplugging the power cable shouldn't cause a network change event - -Revision 1.569 2009/06/03 23:07:12 cheshire -<rdar://problem/6890712> mDNS: iChat's Buddy photo always appears as the "shadow person" over Bonjour -Large records were not being added in cases where an NSEC record was also required - -Revision 1.568 2009/05/19 22:37:04 cheshire -<rdar://problem/6903507> Sleep Proxy: Retransmission logic not working reliably on quiet networks -Added NextScheduledSPRetry field - -Revision 1.567 2009/05/13 17:25:33 mkrochma -<rdar://problem/6879926> Should not schedule maintenance wake when machine has no advertised services -Sleep proxy client should only look for services being advertised via Multicast - -Revision 1.566 2009/05/12 23:09:24 cheshire -<rdar://problem/6879926> Should not schedule maintenance wake when machine has no advertised services -Declare mDNSCoreHaveAdvertisedServices routine so it can be called from daemon.c - -Revision 1.565 2009/05/09 00:10:58 jessic2 -Change expected size of NetworkInterfaceInfo to fix build failure - -Revision 1.564 2009/05/07 23:31:26 cheshire -<rdar://problem/6601427> Sleep Proxy: Retransmit and retry Sleep Proxy Server requests -Added NextSPSAttempt and NextSPSAttemptTime fields to NetworkInterfaceInfo_struct - -Revision 1.563 2009/04/24 21:06:38 cheshire -Added comment about UDP length field (includes UDP header, so minimum value is 8 bytes) - -Revision 1.562 2009/04/24 00:22:23 cheshire -<rdar://problem/3476350> Return negative answers when host knows authoritatively that no answer exists -Added definition of rdataNSEC - -Revision 1.561 2009/04/23 22:06:29 cheshire -Added CacheRecord and InterfaceID parameters to MakeNegativeCacheRecord, in preparation for: -<rdar://problem/3476350> Return negative answers when host knows authoritatively that no answer exists - -Revision 1.560 2009/04/23 21:54:50 cheshire -Updated comments - -Revision 1.559 2009/04/22 00:37:38 cheshire -<rdar://problem/6814427> Remove unused kDNSType_MAC - -Revision 1.558 2009/04/21 23:36:25 cheshire -<rdar://problem/6814427> Remove unused kDNSType_MAC - -Revision 1.557 2009/04/15 20:42:51 mcguire -<rdar://problem/6768947> uDNS: Treat RCODE 5 (Refused) responses as failures - -Revision 1.556 2009/04/11 00:19:43 jessic2 -<rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically - -Revision 1.555 2009/04/01 21:12:27 herscher -<rdar://problem/5925472> Current Bonjour code does not compile on Windows. - -Revision 1.554 2009/04/01 17:50:11 mcguire -cleanup mDNSRandom - -Revision 1.553 2009/03/26 03:59:00 jessic2 -Changes for <rdar://problem/6492552&6492593&6492609&6492613&6492628&6492640&6492699> - -Revision 1.552 2009/03/20 23:53:03 jessic2 -<rdar://problem/6646228> SIGHUP should restart all in-progress queries - -Revision 1.551 2009/03/18 20:41:04 cheshire -Added definition of the all-ones mDNSOpaque16 ID - -Revision 1.550 2009/03/17 19:10:29 mcguire -Fix sizechecks for x86_64 - -Revision 1.549 2009/03/17 01:22:56 cheshire -<rdar://problem/6601427> Sleep Proxy: Retransmit and retry Sleep Proxy Server requests -Initial support for resolving up to three Sleep Proxies in parallel - -Revision 1.548 2009/03/14 01:42:56 mcguire -<rdar://problem/5457116> BTMM: Fix issues with multiple .Mac accounts on the same machine - -Revision 1.547 2009/03/10 01:14:30 cheshire -Sleep Proxies with invalid names need to be ignored (score 10000), -not treated as "Sleep Proxy of last resort" (score 9999) - -Revision 1.546 2009/03/06 23:51:51 mcguire -Fix broken build by defining DiscardPort - -Revision 1.545 2009/03/06 22:39:23 cheshire -<rdar://problem/6655850> Ignore prototype base stations when picking Sleep Proxy to register with - -Revision 1.544 2009/03/04 01:33:30 cheshire -Add m->ProxyRecords counter - -Revision 1.543 2009/03/03 23:04:43 cheshire -For clarity, renamed "MAC" field to "HMAC" (Host MAC, as opposed to Interface MAC) - -Revision 1.542 2009/03/03 22:51:53 cheshire -<rdar://problem/6504236> Sleep Proxy: Waking on same network but different interface will cause conflicts - -Revision 1.541 2009/03/03 00:45:19 cheshire -Added m->PrimaryMAC field - -Revision 1.540 2009/02/27 02:56:57 cheshire -Moved struct SearchListElem definition from uDNS.c into mDNSEmbeddedAPI.h - -Revision 1.539 2009/02/17 23:29:01 cheshire -Throttle logging to a slower rate when running on SnowLeopard - -Revision 1.538 2009/02/11 02:31:57 cheshire -Moved SystemWakeOnLANEnabled from mDNSMacOSX.h, so it's accessible to mDNSCore routines - -Revision 1.537 2009/02/07 02:51:48 cheshire -<rdar://problem/6084043> Sleep Proxy: Need to adopt IOPMConnection -Added new functions and timing variables - -Revision 1.536 2009/01/30 23:50:31 cheshire -Added LastLabel() routine to get the last label of a domainname - -Revision 1.535 2009/01/23 00:38:36 mcguire -<rdar://problem/5570906> BTMM: Doesn't work with Linksys WRT54GS firmware 4.71.1 - -Revision 1.534 2009/01/22 02:14:25 cheshire -<rdar://problem/6515626> Sleep Proxy: Set correct target MAC address, instead of all zeroes - -Revision 1.533 2009/01/21 03:43:57 mcguire -<rdar://problem/6511765> BTMM: Add support for setting kDNSServiceErr_NATPortMappingDisabled in DynamicStore - -Revision 1.532 2009/01/16 19:50:36 cheshire -Oops. Fixed definition of SleepProxyServiceType. - -Revision 1.531 2009/01/16 19:48:09 cheshire -Added definition of SleepProxyServiceType - -Revision 1.530 2009/01/15 00:22:49 mcguire -<rdar://problem/6437092> NAT-PMP: mDNSResponder needs to listen on 224.0.0.1:5350/UDP with REUSEPORT - -Revision 1.529 2009/01/13 00:31:44 cheshire -Fixed off-by-one error in computing the implicit limit pointer in the "DomainNameLength(name)" macro - -Revision 1.528 2009/01/10 01:43:52 cheshire -Changed misleading function name 'AnsweredLOQ' to more informative 'AnsweredLocalQ' - -Revision 1.527 2008/12/12 01:23:19 cheshire -Added m->SPSProxyListChanged state variable to flag when we need to update our BPF filter program - -Revision 1.526 2008/12/12 00:51:14 cheshire -Added structure definitions for IPv6Header, etc. - -Revision 1.525 2008/12/10 02:18:31 cheshire -Increased MaxMsg to 160 for showing longer TXT records in SIGINFO output - -Revision 1.524 2008/12/10 01:49:39 cheshire -Fixes for alignment issues on ARMv5 - -Revision 1.523 2008/12/05 02:35:24 mcguire -<rdar://problem/6107390> Write to the DynamicStore when a Sleep Proxy server is available on the network - -Revision 1.522 2008/12/04 21:08:51 mcguire -<rdar://problem/6116863> mDNS: Provide mechanism to disable Multicast advertisements - -Revision 1.521 2008/12/04 02:19:24 cheshire -Updated comment - -Revision 1.520 2008/11/26 20:28:05 cheshire -Added new SSHPort constant - -Revision 1.519 2008/11/25 22:46:30 cheshire -For ease of code searching, renamed ZoneData field of ServiceRecordSet_struct from "nta" to "srs_nta" - -Revision 1.518 2008/11/25 05:07:15 cheshire -<rdar://problem/6374328> Advertise Sleep Proxy metrics in service name - -Revision 1.517 2008/11/20 01:51:19 cheshire -Exported RecreateNATMappings so it's callable from other files - -Revision 1.516 2008/11/16 16:49:25 cheshire -<rdar://problem/6375808> LLQs broken in mDNSResponder-184 -DNSOpt_LLQData_Space was incorrectly defined to be 18 instead of 22 - -Revision 1.515 2008/11/14 20:59:41 cheshire -Added mDNSEthAddressIsZero(A) macro - -Revision 1.514 2008/11/14 02:17:41 cheshire -Added NextScheduledSPS task scheduling variable - -Revision 1.513 2008/11/14 00:47:19 cheshire -Added TimeRcvd and TimeExpire fields to AuthRecord_struct - -Revision 1.512 2008/11/14 00:00:53 cheshire -After client machine wakes up, Sleep Proxy machine need to remove any records -it was temporarily holding as proxy for that client - -Revision 1.511 2008/11/13 19:04:44 cheshire -Added definition of OwnerOptData - -Revision 1.510 2008/11/06 23:48:32 cheshire -Changed SleepProxyServerType to mDNSu8 - -Revision 1.509 2008/11/04 23:06:50 cheshire -Split RDataBody union definition into RDataBody and RDataBody2, and removed -SOA from the normal RDataBody union definition, saving 270 bytes per AuthRecord - -Revision 1.508 2008/11/04 22:21:43 cheshire -Changed zone field of AuthRecord_struct from domainname to pointer, saving 252 bytes per AuthRecord - -Revision 1.507 2008/11/04 22:13:43 cheshire -Made RDataBody parameter to GetRRDisplayString_rdb "const" - -Revision 1.506 2008/11/04 20:06:19 cheshire -<rdar://problem/6186231> Change MAX_DOMAIN_NAME to 256 - -Revision 1.505 2008/11/03 23:49:47 mkrochma -Increase NATMAP_DEFAULT_LEASE to 2 hours so we do maintenance wake less often - -Revision 1.504 2008/10/31 22:55:04 cheshire -Initial support for structured SPS names - -Revision 1.503 2008/10/24 23:58:47 cheshire -Ports should be mDNSIPPort, not mDNSOpaque16 - -Revision 1.502 2008/10/23 22:25:56 cheshire -Renamed field "id" to more descriptive "updateid" - -Revision 1.501 2008/10/22 22:22:27 cheshire -Added packet structure definitions - -Revision 1.500 2008/10/22 19:55:35 cheshire -Miscellaneous fixes; renamed FindFirstAnswerInCache to FindSPSInCache - -Revision 1.499 2008/10/22 17:15:47 cheshire -Updated definitions of mDNSIPv4AddressIsZero/mDNSIPv4AddressIsOnes, etc. - -Revision 1.498 2008/10/22 01:01:52 cheshire -Added onesEthAddr constant, used for sending ARP broadcasts - -Revision 1.497 2008/10/21 00:51:11 cheshire -Added declaration of mDNSPlatformSetBPF(), used by uds_daemon.c to pass BPF fd to mDNSMacOSX.c - -Revision 1.496 2008/10/16 22:38:52 cheshire -Added declaration of mDNSCoreReceiveRawPacket() - -Revision 1.495 2008/10/15 22:53:51 cheshire -Removed unused "#define LocalReverseMapDomain" - -Revision 1.494 2008/10/15 20:37:17 cheshire -Added "#define DNSOpt_Lease_Space 19" - -Revision 1.493 2008/10/14 21:37:56 cheshire -Removed unnecessary m->BeSleepProxyServer variable - -Revision 1.492 2008/10/14 20:26:36 cheshire -Added definition of a new kDNSType_MAC rdata type - -Revision 1.491 2008/10/09 22:29:04 cheshire -Added "mDNSEthAddr MAC" to NetworkInterfaceInfo_struct - -Revision 1.490 2008/10/09 21:39:20 cheshire -Update list of DNS types - -Revision 1.489 2008/10/09 18:59:19 cheshire -Added NetWakeResolve code, removed unused m->SendDeregistrations and m->SendImmediateAnswers - -Revision 1.488 2008/10/08 01:02:03 cheshire -Added mDNS_SetupQuestion() convenience function - -Revision 1.487 2008/10/07 21:41:57 mcguire -Increase sizecheck limits to account for DNSQuestion added to NetworkInterfaceInfo_struct in 64bit builds - -Revision 1.486 2008/10/07 15:56:24 cheshire -Increase sizecheck limits to account for DNSQuestion added to NetworkInterfaceInfo_struct - -Revision 1.485 2008/10/04 00:48:37 cheshire -Added DNSQuestion to NetworkInterfaceInfo_struct, used for browsing for Sleep Proxy Servers - -Revision 1.484 2008/10/04 00:01:45 cheshire -Move NetworkInterfaceInfo_struct further down in file (we'll need to add a DNSQuestion to it later) - -Revision 1.483 2008/10/03 23:28:41 cheshire -Added declaration of mDNSPlatformSendRawPacket - -Revision 1.482 2008/10/03 17:30:05 cheshire -Added declaration of mDNS_ConfigChanged(mDNS *const m); - -Revision 1.481 2008/10/02 22:38:58 cheshire -Added SleepProxyServer fields, and mDNSCoreBeSleepProxyServer() call for turning SleepProxyServer on and off - -Revision 1.480 2008/10/01 21:22:17 cheshire -Added NetWake field to NetworkInterfaceInfo structure, to signal when Wake-On-Magic-Packet is enabled for that interface - -Revision 1.479 2008/09/29 20:12:37 cheshire -Rename 'AnswerLocalQuestions' to more descriptive 'AnswerLocalOnlyQuestions' and 'AnsweredLocalQ' to 'AnsweredLOQ' - -Revision 1.478 2008/09/23 02:37:10 cheshire -Added FirstLabel/SecondLabel macros - -Revision 1.477 2008/09/20 00:34:22 mcguire -<rdar://problem/6129039> BTMM: Add support for WANPPPConnection - -Revision 1.476 2008/09/05 22:22:01 cheshire -Move "UDPSocket *LocalSocket" field to more logical place in DNSQuestion_struct - -Revision 1.475 2008/07/25 22:34:11 mcguire -fix sizecheck issues for 64bit - -Revision 1.474 2008/07/24 20:23:03 cheshire -<rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning - -Revision 1.473 2008/07/18 21:37:42 mcguire -<rdar://problem/5736845> BTMM: alternate SSDP queries between multicast & unicast - -Revision 1.472 2008/07/01 01:39:59 mcguire -<rdar://problem/5823010> 64-bit fixes - -Revision 1.471 2008/06/26 17:24:11 mkrochma -<rdar://problem/5450912> BTMM: Stop listening on UDP 5351 for NAT Status Announcements - -Revision 1.470 2008/06/19 01:20:49 mcguire -<rdar://problem/4206534> Use all configured DNS servers - -Revision 1.469 2008/03/07 18:56:02 cheshire -<rdar://problem/5777647> dnsbugtest query every three seconds when source IP address of response doesn't match - -Revision 1.468 2008/03/06 02:48:34 mcguire -<rdar://problem/5321824> write status to the DS - -Revision 1.467 2008/02/26 20:48:46 cheshire -Need parentheses around use of macro argument in mDNS_TimeNow_NoLock(m) - -Revision 1.466 2008/02/21 21:36:32 cheshire -Updated comment about record type values (kDNSRecordTypePacketAns/Auth/Add) - -Revision 1.465 2008/02/20 00:39:05 mcguire -<rdar://problem/5427102> Some device info XML blobs too large - -Revision 1.464 2008/01/31 23:33:29 mcguire -<rdar://problem/5614450> changes to build using gcc 4.2 with -Werror - -Revision 1.463 2007/12/17 23:53:25 cheshire -Added DNSDigest_SignMessageHostByteOrder, for signing messages not yet converted to network byte order - -Revision 1.462 2007/12/17 23:48:29 cheshire -DNSDigest_SignMessage doesn't need to return a result -- it already updates the 'end' parameter - -Revision 1.461 2007/12/15 00:18:51 cheshire -Renamed question->origLease to question->ReqLease - -Revision 1.460 2007/12/14 23:55:28 cheshire -Moved "struct tcpInfo_t" definition from uDNS.c to mDNSEmbeddedAPI.h - -Revision 1.459 2007/12/07 22:40:34 cheshire -Rename 'LocalAnswer' to more descriptive 'AnsweredLocalQ' - -Revision 1.458 2007/12/07 00:45:58 cheshire -<rdar://problem/5526800> BTMM: Need to clean up registrations on shutdown - -Revision 1.457 2007/12/06 00:22:27 mcguire -<rdar://problem/5604567> BTMM: Doesn't work with Linksys WAG300N 1.01.06 (sending from 1026/udp) - -Revision 1.456 2007/12/05 01:45:35 cheshire -Renamed markedForDeletion -> MarkedForDeletion - -Revision 1.455 2007/12/01 01:21:27 jgraessley -<rdar://problem/5623140> mDNSResponder unicast DNS improvements - -Revision 1.454 2007/12/01 00:34:03 cheshire -Fixes from Bob Bradley for building on EFI - -Revision 1.453 2007/10/29 23:51:22 cheshire -Added comment about NATTraversalInfo ExternalAddress field - -Revision 1.452 2007/10/29 18:13:40 cheshire -Added Question_uDNS macro, analogous to AuthRecord_uDNS macro - -Revision 1.451 2007/10/26 23:42:57 cheshire -Removed unused "mDNSs32 expire" field from ServiceRecordSet_struct - -Revision 1.450 2007/10/26 22:24:08 cheshire -Added AuthRecord_uDNS() macro to determine when a given AuthRecord needs to be registered via unicast DNS - -Revision 1.449 2007/10/25 20:48:47 cheshire -For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer' - -Revision 1.448 2007/10/22 22:19:44 cheshire -Tidied up code alignment - -Revision 1.447 2007/10/22 19:40:30 cheshire -<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep -Made subroutine mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst) - -Revision 1.446 2007/10/17 22:49:54 cheshire -<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep - -Revision 1.445 2007/10/17 22:37:23 cheshire -<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events - -Revision 1.444 2007/09/29 03:14:52 cheshire -<rdar://problem/5513168> BTMM: mDNSResponder memory corruption in GetAuthInfoForName_internal -Added AutoTunnelUnregistered macro to check state of DomainAuthInfo AuthRecords - -Revision 1.443 2007/09/27 21:21:39 cheshire -Export CompleteDeregistration so it's callable from other files - -Revision 1.442 2007/09/27 00:25:39 cheshire -Added ttl_seconds parameter to MakeNegativeCacheRecord in preparation for: -<rdar://problem/4947392> uDNS: Use SOA to determine TTL for negative answers - -Revision 1.441 2007/09/26 23:17:49 cheshire -Get rid of unused kWideAreaTTL constant - -Revision 1.440 2007/09/26 22:06:02 cheshire -<rdar://problem/5507399> BTMM: No immediate failure notifications for BTMM names - -Revision 1.439 2007/09/21 21:12:36 cheshire -DNSDigest_SignMessage does not need separate "mDNSu16 *numAdditionals" parameter - -Revision 1.438 2007/09/19 20:32:09 cheshire -Export GetAuthInfoForName so it's callable from other files - -Revision 1.437 2007/09/18 21:42:29 cheshire -To reduce programming mistakes, renamed ExtPort to RequestedPort - -Revision 1.436 2007/09/14 21:26:08 cheshire -<rdar://problem/5482627> BTMM: Need to manually avoid port conflicts when using UPnP gateways - -Revision 1.435 2007/09/13 00:16:41 cheshire -<rdar://problem/5468706> Miscellaneous NAT Traversal improvements - -Revision 1.434 2007/09/12 23:03:07 cheshire -<rdar://problem/5476978> DNSServiceNATPortMappingCreate callback not giving correct interface index - -Revision 1.433 2007/09/12 22:19:28 cheshire -<rdar://problem/5476977> Need to listen for port 5350 NAT-PMP announcements - -Revision 1.432 2007/09/12 19:22:19 cheshire -Variable renaming in preparation for upcoming fixes e.g. priv/pub renamed to intport/extport -Made NAT Traversal packet handlers take typed data instead of anonymous "mDNSu8 *" byte pointers - -Revision 1.431 2007/09/11 19:19:16 cheshire -Correct capitalization of "uPNP" to "UPnP" - -Revision 1.430 2007/09/10 22:06:50 cheshire -Rename uptime => upseconds and LastNATUptime => LastNATupseconds to make it clear these time values are in seconds - -Revision 1.429 2007/09/07 21:16:58 cheshire -Add new symbol "NATPMPAnnouncementPort" (5350) - -Revision 1.428 2007/09/05 21:48:01 cheshire -<rdar://problem/5385864> BTMM: mDNSResponder flushes wide-area Bonjour records after an hour for a zone. -Now that we're respecting the TTL of uDNS records in the cache, the LLQ maintenance code needs -to update the cache lifetimes of all relevant records every time it successfully renews an LLQ, -otherwise those records will expire and vanish from the cache. - -Revision 1.427 2007/09/05 20:47:12 cheshire -Tidied up alignment of code layout - -Revision 1.426 2007/09/04 20:37:06 cheshire -<rdar://problem/5457287> mDNSResponder taking up 100% CPU in ReissueBlockedQuestions -Reorder fields into more logical order, with AuthInfo before DuplicateOf - -Revision 1.425 2007/08/31 19:53:14 cheshire -<rdar://problem/5431151> BTMM: IPv6 address lookup should not succeed if autotunnel cannot be setup -If AutoTunnel setup fails, the code now generates a fake NXDomain error saying that the requested AAAA record does not exist - -Revision 1.424 2007/08/31 18:49:49 vazquez -<rdar://problem/5393719> BTMM: Need to properly deregister when stopping BTMM - -Revision 1.423 2007/08/31 00:04:28 cheshire -Added comment explaining deltime in DomainAuthInfo structure - -Revision 1.422 2007/08/28 23:58:42 cheshire -Rename HostTarget -> AutoTarget - -Revision 1.421 2007/08/27 20:30:43 cheshire -Only include TunnelClients list when building for OS X - -Revision 1.420 2007/08/23 21:47:09 vazquez -<rdar://problem/5427316> BTMM: mDNSResponder sends NAT-PMP packets on public network -make sure we clean up port mappings on base stations by sending a lease value of 0, -and only send NAT-PMP packets on private networks; also save some memory by -not using packet structs in NATTraversals. - -Revision 1.419 2007/08/08 21:07:47 vazquez -<rdar://problem/5244687> BTMM: Need to advertise model information via wide-area bonjour - -Revision 1.418 2007/08/01 16:09:13 cheshire -Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly - -Revision 1.417 2007/08/01 03:04:59 cheshire -Add NATTraversalInfo structures to HostnameInfo and DomainAuthInfo - -Revision 1.416 2007/08/01 00:04:13 cheshire -<rdar://problem/5261696> Crash in tcpKQSocketCallback -Half-open TCP connections were not being cancelled properly - -Revision 1.415 2007/07/31 02:28:35 vazquez -<rdar://problem/3734269> NAT-PMP: Detect public IP address changes and base station reboot - -Revision 1.414 2007/07/30 23:34:19 cheshire -Remove unused "udpSock" from DNSQuestion - -Revision 1.413 2007/07/28 01:25:56 cheshire -<rdar://problem/4780038> BTMM: Add explicit UDP event port to LLQ setup request, to fix LLQs not working behind NAT - -Revision 1.412 2007/07/27 23:57:23 cheshire -Added compile-time structure size checks - -Revision 1.411 2007/07/27 22:50:08 vazquez -Allocate memory for UPnP request and reply buffers instead of using arrays - -Revision 1.410 2007/07/27 19:37:19 cheshire -Moved AutomaticBrowseDomainQ into main mDNS object - -Revision 1.409 2007/07/27 19:30:39 cheshire -Changed mDNSQuestionCallback parameter from mDNSBool to QC_result, -to properly reflect tri-state nature of the possible responses - -Revision 1.408 2007/07/27 18:44:01 cheshire -Rename "AnswerQuestionWithResourceRecord" to more informative "AnswerCurrentQuestionWithResourceRecord" - -Revision 1.407 2007/07/26 21:19:26 vazquez -Retry port mapping with incremented port number (up to max) in order to handle -port mapping conflicts on UPnP gateways - -Revision 1.406 2007/07/25 22:19:59 cheshire -ClientTunnel structure also needs a rmt_outer_port field - -Revision 1.405 2007/07/25 03:05:02 vazquez -Fixes for: -<rdar://problem/5338913> LegacyNATTraversal: UPnP heap overflow -<rdar://problem/5338933> LegacyNATTraversal: UPnP stack buffer overflow -and a myriad of other security problems - -Revision 1.404 2007/07/24 20:22:07 cheshire -Add AutoTunnelHostAddrActive flag - -Revision 1.403 2007/07/24 04:14:29 cheshire -<rdar://problem/5356281> LLQs not working in with NAT Traversal - -Revision 1.402 2007/07/21 00:54:44 cheshire -<rdar://problem/5344576> Delay IPv6 address callback until AutoTunnel route and policy is configured - -Revision 1.401 2007/07/20 20:01:38 cheshire -Rename "mDNS_DomainTypeBrowseLegacy" as "mDNS_DomainTypeBrowseAutomatic" - -Revision 1.400 2007/07/20 00:54:18 cheshire -<rdar://problem/4641118> Need separate SCPreferences for per-user .Mac settings - -Revision 1.399 2007/07/18 03:22:35 cheshire -SetupLocalAutoTunnelInterface_internal needs to be callable from uDNS.c - -Revision 1.398 2007/07/18 02:26:56 cheshire -Don't need to declare UpdateTunnels here - -Revision 1.397 2007/07/18 01:03:50 cheshire -<rdar://problem/5303834> Automatically configure IPSec policy when resolving services -Add list of client tunnels so we can automatically reconfigure when local address changes - -Revision 1.396 2007/07/16 23:54:48 cheshire -<rdar://problem/5338850> Crash when removing or changing DNS keys - -Revision 1.395 2007/07/16 20:12:33 vazquez -<rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite - -Revision 1.394 2007/07/12 02:51:27 cheshire -<rdar://problem/5303834> Automatically configure IPSec policy when resolving services - -Revision 1.393 2007/07/11 23:43:42 cheshire -Rename PurgeCacheResourceRecord to mDNS_PurgeCacheResourceRecord - -Revision 1.392 2007/07/11 22:44:40 cheshire -<rdar://problem/5328801> SIGHUP should purge the cache - -Revision 1.391 2007/07/11 20:30:45 cheshire -<rdar://problem/5304766> Register IPSec tunnel with IPv4-only hostname and create NAT port mappings -Added AutoTunnelTarget and AutoTunnelService to DomainAuthInfo structure - -Revision 1.390 2007/07/11 18:56:55 cheshire -Added comments about AutoTunnelHostAddr and AutoTunnelLabel - -Revision 1.389 2007/07/11 02:44:03 cheshire -<rdar://problem/5303807> Register IPv6-only hostname and don't create port mappings for AutoTunnel services -Added AutoTunnel fields to structures - -Revision 1.388 2007/07/10 01:53:18 cheshire -<rdar://problem/5196524> uDNS: mDNSresponder is leaking TCP connections to DNS server -AuthRecord, ServiceRecordSet, and DNSQuestion structures need tcpInfo_t pointers -so they can keep track of what TCP connections they open - -Revision 1.387 2007/07/06 18:55:15 cheshire -Add explicit NextScheduledNATOp scheduling variable - -Revision 1.386 2007/07/03 20:54:11 cheshire -Tidied up code layout of NATTraversalInfo_struct fields and comments - -Revision 1.385 2007/07/03 00:40:23 vazquez -More changes for <rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes) -Safely deal with packet replies and client callbacks - -Revision 1.384 2007/06/29 00:08:07 vazquez -<rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes) - -Revision 1.383 2007/06/20 01:10:12 cheshire -<rdar://problem/5280520> Sync iPhone changes into main mDNSResponder code - -Revision 1.382 2007/06/19 20:31:59 cheshire -Add DNSServer_Disabled state -Add mDNSInterfaceID for DNS servers reachable over specific interfaces - -Revision 1.381 2007/06/15 18:11:16 cheshire -<rdar://problem/5174466> mDNSResponder crashed in memove() near end of MobileSafari stress test -Made AssignDomainName more defensive when source name is garbage - -Revision 1.380 2007/05/25 00:04:51 cheshire -Added comment explaining rdlength - -Revision 1.379 2007/05/21 18:04:40 cheshire -Updated comments -- port_mapping_create_reply renamed to port_mapping_reply - -Revision 1.378 2007/05/17 19:11:46 cheshire -Tidy up code layout - -Revision 1.377 2007/05/15 00:43:33 cheshire -Remove unused regState_Cancelled - -Revision 1.376 2007/05/14 23:51:49 cheshire -Added constants MAX_REVERSE_MAPPING_NAME_V4 and MAX_REVERSE_MAPPING_NAME_V6 - -Revision 1.375 2007/05/10 21:19:18 cheshire -Rate-limit DNS test queries to at most one per three seconds -(useful when we have a dozen active WAB queries, and then we join a new network) - -Revision 1.374 2007/05/07 22:07:47 cheshire -<rdar://problem/4738025> Enhance GetLargeResourceRecord to decompress more record types - -Revision 1.373 2007/05/07 20:43:45 cheshire -<rdar://problem/4241419> Reduce the number of queries and announcements - -Revision 1.372 2007/05/04 22:15:29 cheshire -Get rid of unused q->RestartTime - -Revision 1.371 2007/05/03 22:40:37 cheshire -<rdar://problem/4669229> mDNSResponder ignores bogus null target in SRV record - -Revision 1.370 2007/05/02 22:18:09 cheshire -Renamed NATTraversalInfo_struct context to NATTraversalContext - -Revision 1.369 2007/05/01 21:21:42 cheshire -Add missing parentheses in LEASE_OPT_RDLEN definition - -Revision 1.368 2007/04/30 21:33:38 cheshire -Fix crash when a callback unregisters a service while the UpdateSRVRecords() loop -is iterating through the m->ServiceRegistrations list - -Revision 1.367 2007/04/28 01:31:59 cheshire -Improve debugging support for catching memory corruption problems - -Revision 1.366 2007/04/27 19:28:02 cheshire -Any code that calls StartGetZoneData needs to keep a handle to the structure, so -it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop --- it would start a query and then quickly cancel it, and then when -StartGetZoneData completed, it had a dangling pointer and crashed.) - -Revision 1.365 2007/04/26 00:35:15 cheshire -<rdar://problem/5140339> uDNS: Domain discovery not working over VPN -Fixes to make sure results update correctly when connectivity changes (e.g. a DNS server -inside the firewall may give answers where a public one gives none, and vice versa.) - -Revision 1.364 2007/04/24 02:07:42 cheshire -<rdar://problem/4246187> Identical client queries should reference a single shared core query -Deleted some more redundant code - -Revision 1.363 2007/04/24 00:09:47 cheshire -Remove MappedV4 field from mDNS_struct (not actually used anywhere) - -Revision 1.362 2007/04/22 06:02:02 cheshire -<rdar://problem/4615977> Query should immediately return failure when no server - -Revision 1.361 2007/04/21 19:43:33 cheshire -Code tidying: represent NAT opcodes as bitwise combinations rather than numerical additions - -Revision 1.360 2007/04/20 21:17:24 cheshire -For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative - -Revision 1.359 2007/04/19 22:50:53 cheshire -<rdar://problem/4246187> Identical client queries should reference a single shared core query - -Revision 1.358 2007/04/19 20:06:41 cheshire -Rename field 'Private' (sounds like a boolean) to more informative 'AuthInfo' (it's a DomainAuthInfo pointer) - -Revision 1.357 2007/04/19 18:14:51 cheshire -In mDNS_AddSearchDomain_CString check for NULL pointer before calling MakeDomainNameFromDNSNameString() - -Revision 1.356 2007/04/18 20:56:46 cheshire -Added mDNS_AddSearchDomain_CString macro - -Revision 1.355 2007/04/17 19:21:29 cheshire -<rdar://problem/5140339> Domain discovery not working over VPN - -Revision 1.354 2007/04/05 22:55:34 cheshire -<rdar://problem/5077076> Records are ending up in Lighthouse without expiry information - -Revision 1.353 2007/04/05 20:40:37 cheshire -Remove unused mDNSPlatformTCPGetFlags() - -Revision 1.352 2007/04/04 21:48:52 cheshire -<rdar://problem/4720694> Combine unicast authoritative answer list with multicast list - -Revision 1.351 2007/04/04 01:27:45 cheshire -Update comment - -Revision 1.350 2007/04/04 00:03:26 cheshire -<rdar://problem/5089862> DNSServiceQueryRecord is returning kDNSServiceErr_NoSuchRecord for empty rdata - -Revision 1.349 2007/04/03 19:37:58 cheshire -Rename mDNSAddrIsv4Private() to more precise mDNSAddrIsRFC1918() - -Revision 1.348 2007/04/03 19:13:39 cheshire -Added macros mDNSSameIPPort, mDNSSameOpaque16, mDNSIPPortIsZero, mDNSOpaque16IsZero - -Revision 1.347 2007/03/28 20:59:26 cheshire -<rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr() - -Revision 1.346 2007/03/28 15:56:37 cheshire -<rdar://problem/5085774> Add listing of NAT port mapping and GetAddrInfo requests in SIGINFO output - -Revision 1.345 2007/03/22 19:29:23 cheshire -Add comment and check to ensure StandardAuthRDSize is at least 256 bytes - -Revision 1.344 2007/03/22 18:31:48 cheshire -Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy - -Revision 1.343 2007/03/22 00:49:20 cheshire -<rdar://problem/4848295> Advertise model information via Bonjour - -Revision 1.342 2007/03/21 23:06:00 cheshire -Rename uDNS_HostnameInfo to HostnameInfo; deleted some unused fields - -Revision 1.341 2007/03/21 20:44:11 cheshire -Added mDNSAddressIsv4LinkLocal macro - -Revision 1.340 2007/03/21 00:30:02 cheshire -<rdar://problem/4789455> Multiple errors in DNameList-related code - -Revision 1.339 2007/03/20 17:07:15 cheshire -Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket" - -Revision 1.338 2007/03/10 02:28:28 cheshire -Added comment explaining NATResponseHndlr - -Revision 1.337 2007/03/10 02:02:58 cheshire -<rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache -Eliminate unnecessary "InternalResponseHndlr responseCallback" function pointer - -Revision 1.336 2007/02/28 22:12:24 cheshire -Get rid of unused mDNSVal32 and mDNSOpaque32fromIntVal - -Revision 1.335 2007/02/28 21:49:07 cheshire -Off-by-one error: SameDomainLabelCS (case-sensitive) was stopping one character short of -the end of the label, e.g. it would fail to detect that "chesh1" and "chesh2" are different. - -Revision 1.334 2007/02/28 01:44:26 cheshire -<rdar://problem/5027863> Byte order bugs in uDNS.c, uds_daemon.c, dnssd_clientstub.c - -Revision 1.333 2007/02/27 22:55:22 cheshire -Get rid of unused AllDNSLinkGroupv4 and AllDNSLinkGroupv6 - -Revision 1.332 2007/02/27 02:48:24 cheshire -Parameter to LNT_GetPublicIP function is IPv4 address, not anonymous "mDNSOpaque32" object - -Revision 1.331 2007/02/14 03:16:39 cheshire -<rdar://problem/4789477> Eliminate unnecessary malloc/free in mDNSCore code - -Revision 1.330 2007/02/08 21:12:28 cheshire -<rdar://problem/4386497> Stop reading /etc/mDNSResponder.conf on every sleep/wake - -Revision 1.329 2007/02/07 01:19:36 cheshire -<rdar://problem/4849427> API: Reconcile conflicting error code values - -Revision 1.328 2007/01/25 00:19:40 cheshire -Add CNAMEReferrals field to DNSQuestion_struct - -Revision 1.327 2007/01/23 02:56:10 cheshire -Store negative results in the cache, instead of generating them out of pktResponseHndlr() - -Revision 1.326 2007/01/20 01:30:49 cheshire -Update comments - -Revision 1.325 2007/01/19 18:39:11 cheshire -Fix a bunch of parameters that should have been declared "const" - -Revision 1.324 2007/01/19 18:04:04 cheshire -For naming consistency, use capital letters for RR types: rdataOpt should be rdataOPT - -Revision 1.323 2007/01/17 21:46:02 cheshire -Remove redundant duplicated "isPrivate" field from LLQ_Info - -Revision 1.322 2007/01/10 22:51:56 cheshire -<rdar://problem/4917539> Add support for one-shot private queries as well as long-lived private queries - -Revision 1.321 2007/01/09 22:37:18 cheshire -Provide ten-second grace period for deleted keys, to give mDNSResponder -time to delete host name before it gives up access to the required key. - -Revision 1.320 2007/01/05 08:30:41 cheshire -Trim excessive "Log" checkin history from before 2006 -(checkin history still available via "cvs log ..." of course) - -Revision 1.319 2007/01/04 23:11:11 cheshire -<rdar://problem/4720673> uDNS: Need to start caching unicast records -When an automatic browsing domain is removed, generate appropriate "remove" events for legacy queries - -Revision 1.318 2007/01/04 20:57:48 cheshire -Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates) - -Revision 1.317 2007/01/04 02:39:53 cheshire -<rdar://problem/4030599> Hostname passed into DNSServiceRegister ignored for Wide-Area service registrations - -Revision 1.316 2006/12/22 20:59:49 cheshire -<rdar://problem/4742742> Read *all* DNS keys from keychain, - not just key for the system-wide default registration domain - -Revision 1.315 2006/12/20 04:07:35 cheshire -Remove uDNS_info substructure from AuthRecord_struct - -Revision 1.314 2006/12/19 22:49:23 cheshire -Remove uDNS_info substructure from ServiceRecordSet_struct - -Revision 1.313 2006/12/19 02:38:20 cheshire -Get rid of unnecessary duplicate query ID field from DNSQuestion_struct - -Revision 1.312 2006/12/19 02:18:48 cheshire -Get rid of unnecessary duplicate "void *context" field from DNSQuestion_struct - -Revision 1.311 2006/12/16 01:58:31 cheshire -<rdar://problem/4720673> uDNS: Need to start caching unicast records - -Revision 1.310 2006/12/15 19:09:56 cheshire -<rdar://problem/4769083> ValidateRData() should be stricter about malformed MX and SRV records -Made DomainNameLength() more defensive by adding a limit parameter, so it can be -safely used to inspect potentially malformed data received from external sources. -Without this, a domain name that starts off apparently valid, but extends beyond the end of -the received packet data, could have appeared valid if the random bytes are already in memory -beyond the end of the packet just happened to have reasonable values (e.g. all zeroes). - -Revision 1.309 2006/12/14 03:02:37 cheshire -<rdar://problem/4838433> Tools: dns-sd -G 0 only returns IPv6 when you have a routable IPv6 address - -Revision 1.308 2006/11/30 23:07:56 herscher -<rdar://problem/4765644> uDNS: Sync up with Lighthouse changes for Private DNS - -Revision 1.307 2006/11/18 05:01:30 cheshire -Preliminary support for unifying the uDNS and mDNS code, -including caching of uDNS answers - -Revision 1.306 2006/11/10 07:44:04 herscher -<rdar://problem/4825493> Fix Daemon locking failures while toggling BTMM - -Revision 1.305 2006/11/10 00:54:15 cheshire -<rdar://problem/4816598> Changing case of Computer Name doesn't work - -Revision 1.304 2006/10/20 05:35:05 herscher -<rdar://problem/4720713> uDNS: Merge unicast active question list with multicast list. - -Revision 1.303 2006/10/04 21:37:33 herscher -Remove uDNS_info substructure from DNSQuestion_struct - -Revision 1.302 2006/09/26 01:53:25 herscher -<rdar://problem/4245016> NAT Port Mapping API (for both NAT-PMP and UPnP Gateway Protocol) - -Revision 1.301 2006/09/15 21:20:15 cheshire -Remove uDNS_info substructure from mDNS_struct - -Revision 1.300 2006/08/14 23:24:23 cheshire -Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 - -Revision 1.299 2006/07/15 02:01:28 cheshire -<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder -Fix broken "empty string" browsing - -Revision 1.298 2006/07/05 22:55:03 cheshire -<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder -Need Private field in uDNS_RegInfo - -Revision 1.297 2006/07/05 22:20:03 cheshire -<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder - -Revision 1.296 2006/06/29 05:28:01 cheshire -Added comment about mDNSlocal and mDNSexport - -Revision 1.295 2006/06/29 03:02:43 cheshire -<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support - -Revision 1.294 2006/06/28 06:50:08 cheshire -In future we may want to change definition of mDNSs32 from "signed long" to "signed int" -I doubt anyone is building mDNSResponder on systems where int is 16-bits, -but lets add a compile-time assertion to make sure. - -Revision 1.293 2006/06/12 18:00:43 cheshire -To make code a little more defensive, check _ILP64 before _LP64, -in case both are set by mistake on some platforms - -Revision 1.292 2006/03/19 17:00:57 cheshire -Define symbol MaxMsg instead of using hard-coded constant value '80' - -Revision 1.291 2006/03/19 02:00:07 cheshire -<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions - -Revision 1.290 2006/03/08 22:42:23 cheshire -Fix spelling mistake: LocalReverseMapomain -> LocalReverseMapDomain - -Revision 1.289 2006/02/26 00:54:41 cheshire -Fixes to avoid code generation warning/error on FreeBSD 7 - -*/ + */ #ifndef __mDNSClientAPI_h #define __mDNSClientAPI_h @@ -1207,6 +279,12 @@ typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identi typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque identifier (not an integer) +// Bit operations for opaque 64 bit quantity. Uses the 32 bit quantity(l[2]) to set and clear bits +#define mDNSNBBY 8 +#define bit_set_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] |= (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) +#define bit_clr_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] &= ~(1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) +#define bit_get_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] & (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) + enum { mDNSAddrType_None = 0, @@ -1215,6 +293,13 @@ enum mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording }; +enum + { + mDNSTransport_None = 0, + mDNSTransport_UDP = 1, + mDNSTransport_TCP = 2 + }; + typedef struct { mDNSs32 type; @@ -1286,7 +371,8 @@ typedef mDNSs32 mStatus; #define MAX_DOMAIN_LABEL 63 typedef struct { mDNSu8 c[ 64]; } domainlabel; // One label: length byte and up to 63 characters -// RFC 1034/1035/2181 specify that a domain name, including length bytes, data bytes, and terminating zero, may be up to 256 bytes long +// RFC 1034/1035/2181 specify that a domain name (length bytes and data bytes) may be up to 255 bytes long, +// plus the terminating zero at the end makes 256 bytes total in the on-the-wire format. #define MAX_DOMAIN_NAME 256 typedef struct { mDNSu8 c[256]; } domainname; // Up to 256 bytes of length-prefixed domainlabels @@ -1323,6 +409,18 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string #define kStandardTTL (3600UL * 100 / 80) #define kHostNameTTL 120UL +// Multicast DNS uses announcements (gratuitous responses) to update peer caches. +// This means it is feasible to use relatively larger TTL values than we might otherwise +// use, because we have a cache coherency protocol to keep the peer caches up to date. +// With Unicast DNS, once an authoritative server gives a record with a certain TTL value to a client +// or caching server, that client or caching server is entitled to hold onto the record until its TTL +// expires, and has no obligation to contact the authoritative server again until that time arrives. +// This means that whereas Multicast DNS can use announcements to pre-emptively update stale data +// before it would otherwise have expired, standard Unicast DNS (not using LLQs) has no equivalent +// mechanism, and TTL expiry is the *only* mechanism by which stale data gets deleted. Because of this, +// we currently limit the TTL to ten seconds in such cases where no dynamic cache updating is possible. +#define kStaticCacheTTL 10 + #define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL) typedef struct AuthRecord_struct AuthRecord; @@ -1378,10 +476,10 @@ typedef struct tcpInfo_t DNSMessage request; int requestLen; DNSQuestion *question; // For queries - ServiceRecordSet *srs; // For service record updates AuthRecord *rr; // For record updates mDNSAddr Addr; mDNSIPPort Port; + mDNSIPPort SrcPort; DNSMessage *reply; mDNSu16 replylen; unsigned long nread; @@ -1422,7 +520,7 @@ typedef packedstruct mDNSOpaque16 id; mDNSOpaque16 flagsfrags; mDNSu8 ttl; - mDNSu8 protocol; + mDNSu8 protocol; // Payload type: 0x06 = TCP, 0x11 = UDP mDNSu16 checksum; mDNSv4Addr src; mDNSv4Addr dst; @@ -1432,7 +530,7 @@ typedef packedstruct { mDNSu32 vcf; // Version, Traffic Class, Flow Label mDNSu16 len; // Payload Length - mDNSu8 protocol; // Type of next header: 0x06 = TCP, 0x11 = UDP, 0x3A = ICMPv6 + mDNSu8 pro; // Type of next header: 0x06 = TCP, 0x11 = UDP, 0x3A = ICMPv6 mDNSu8 ttl; // Hop Limit mDNSv6Addr src; mDNSv6Addr dst; @@ -1440,20 +538,19 @@ typedef packedstruct typedef packedstruct { - mDNSu8 type; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement - mDNSu8 code; - mDNSu16 checksum; - mDNSu32 reserved; - mDNSv6Addr target; - } IPv6ND; // 24 bytes + mDNSv6Addr src; + mDNSv6Addr dst; + mDNSOpaque32 len; + mDNSOpaque32 pro; + } IPv6PseudoHeader; // 40 bytes -typedef packedstruct +typedef union { - mDNSIPPort src; - mDNSIPPort dst; - mDNSu16 len; // Length including UDP header (ie. minimum value is 8 bytes) - mDNSu16 checksum; - } UDPHeader; // 8 bytes + mDNSu8 bytes[20]; + ARP_EthIP arp; + IPv4Header v4; + IPv6Header v6; + } NetworkLayerPacket; typedef packedstruct { @@ -1466,7 +563,55 @@ typedef packedstruct mDNSu16 window; mDNSu16 checksum; mDNSu16 urgent; - } TCPHeader; // 20 bytes + } TCPHeader; // 20 bytes; IP protocol type 0x06 + +typedef packedstruct + { + mDNSIPPort src; + mDNSIPPort dst; + mDNSu16 len; // Length including UDP header (i.e. minimum value is 8 bytes) + mDNSu16 checksum; + } UDPHeader; // 8 bytes; IP protocol type 0x11 + +typedef packedstruct + { + mDNSu8 type; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement + mDNSu8 code; + mDNSu16 checksum; + mDNSu32 flags_res; // R/S/O flags and reserved bits + mDNSv6Addr target; + // Typically 8 bytes of options are also present + } IPv6NDP; // 24 bytes or more; IP protocol type 0x3A + +#define NDP_Sol 0x87 +#define NDP_Adv 0x88 + +#define NDP_Router 0x80 +#define NDP_Solicited 0x40 +#define NDP_Override 0x20 + +#define NDP_SrcLL 1 +#define NDP_TgtLL 2 + +typedef union + { + mDNSu8 bytes[20]; + TCPHeader tcp; + UDPHeader udp; + IPv6NDP ndp; + } TransportLayerPacket; + +typedef packedstruct + { + mDNSOpaque64 InitiatorCookie; + mDNSOpaque64 ResponderCookie; + mDNSu8 NextPayload; + mDNSu8 Version; + mDNSu8 ExchangeType; + mDNSu8 Flags; + mDNSOpaque32 MessageID; + mDNSu32 Length; + } IKEHeader; // 28 bytes // *************************************************************************** #if 0 @@ -1541,7 +686,9 @@ enum kDNSRecordTypeKnownUnique = 0x20, // Known Unique means mDNS can assume name is unique without checking // For Dynamic Update records, Known Unique means the record must already exist on the server. kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveSharedMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared), + kDNSRecordTypeActiveUniqueMask = (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveMask = (kDNSRecordTypeActiveSharedMask | kDNSRecordTypeActiveUniqueMask), kDNSRecordTypePacketAdd = 0x80, // Received in the Additional Section of a DNS Response kDNSRecordTypePacketAddUnique = 0x90, // Received in the Additional Section of a DNS Response with kDNSClass_UniqueRRSet set @@ -1552,7 +699,7 @@ enum kDNSRecordTypePacketNegative = 0xF0, // Pseudo-RR generated to cache non-existence results like NXDomain - kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique + kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique, kDNSRecordTypePacketNegative }; typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; @@ -1625,16 +772,12 @@ typedef packedstruct (X) == DNSOpt_OwnerData_ID_Wake_PW4_Space - 4 || \ (X) == DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 ) -#define ValidDNSOpt(O) (((O)->opt == kDNSOpt_LLQ && (O)->optlen == DNSOpt_LLQData_Space - 4) || \ - ((O)->opt == kDNSOpt_Lease && (O)->optlen == DNSOpt_LeaseData_Space - 4) || \ - ((O)->opt == kDNSOpt_Owner && ValidOwnerLength((O)->optlen) ) ) - -#define DNSOpt_Owner_Space(O) (mDNSSameEthAddress(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space) +#define DNSOpt_Owner_Space(A,B) (mDNSSameEthAddress((A),(B)) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space) #define DNSOpt_Data_Space(O) ( \ (O)->opt == kDNSOpt_LLQ ? DNSOpt_LLQData_Space : \ (O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space : \ - (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(O) : 0x10000) + (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000) // A maximal NSEC record is: // 256 bytes domainname 'nextname' @@ -1669,15 +812,15 @@ typedef struct // On 64-bit, the pointers in a CacheRecord are bigger, and that creates 8 bytes more space for the name in a CacheGroup #if ENABLE_MULTI_PACKET_QUERY_SNOOPING #if defined(_ILP64) || defined(__ILP64__) || defined(_LP64) || defined(__LP64__) || defined(_WIN64) - #define InlineCacheGroupNameSize 152 + #define InlineCacheGroupNameSize 160 #else - #define InlineCacheGroupNameSize 144 + #define InlineCacheGroupNameSize 148 #endif #else #if defined(_ILP64) || defined(__ILP64__) || defined(_LP64) || defined(__LP64__) || defined(_WIN64) - #define InlineCacheGroupNameSize 136 + #define InlineCacheGroupNameSize 144 #else - #define InlineCacheGroupNameSize 128 + #define InlineCacheGroupNameSize 132 #endif #endif @@ -1737,7 +880,7 @@ typedef void mDNSRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus res // Restrictions: An mDNSRecordUpdateCallback may not make any mDNS API calls. // The intent of this callback is to allow the client to free memory, if necessary. // The internal data structures of the mDNS code may not be in a state where mDNS API calls may be made safely. -typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData); +typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData, mDNSu16 OldRDLen); // *************************************************************************** #if 0 @@ -1829,9 +972,9 @@ struct tcpLNTInfo_struct LNTOp_t op; // operation performed using this connection mDNSAddr Address; // router address mDNSIPPort Port; // router port - mDNSs8 *Request; // xml request to router + mDNSu8 *Request; // xml request to router int requestLen; - mDNSs8 *Reply; // xml reply from router + mDNSu8 *Reply; // xml reply from router int replyLen; unsigned long nread; // number of bytes read so far int retries; // number of times we've tried to do this port mapping @@ -1882,6 +1025,36 @@ struct NATTraversalInfo_struct void *clientContext; }; +enum + { + DNSServer_Untested = 0, + DNSServer_Passed = 1, + DNSServer_Failed = 2, + DNSServer_Disabled = 3 + }; + +enum + { + DNSServer_FlagDelete = 1, + DNSServer_FlagNew = 2 + }; + +typedef struct DNSServer + { + struct DNSServer *next; + mDNSInterfaceID interface; // For specialized uses; we can have DNS servers reachable over specific interfaces + mDNSAddr addr; + mDNSIPPort port; + mDNSOpaque16 testid; + mDNSu32 flags; // Set when we're planning to delete this from the list + mDNSu32 teststate; // Have we sent bug-detection query to this server? + mDNSs32 lasttest; // Time we sent last bug-detection query to this server + domainname domain; // name->server matching for "split dns" + mDNSs32 penaltyTime; // amount of time this server is penalized + mDNSBool scoped; // interface should be matched against question only + // if scoped is set + } DNSServer; + typedef struct // Size is 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit { mDNSu8 RecordType; // See enum above @@ -1907,24 +1080,22 @@ typedef struct // Size is 36 bytes when compiling for 32-bit; 48 when comp // that are interface-specific (e.g. address records, especially linklocal addresses) const domainname *name; RData *rdata; // Pointer to storage for this rdata + DNSServer *rDNSServer; // Unicast DNS server authoritative for this entry;null for multicast } ResourceRecord; // Unless otherwise noted, states may apply to either independent record registrations or service registrations typedef enum { regState_Zero = 0, - regState_FetchingZoneData = 1, // getting info - update not sent - regState_Pending = 2, // update sent, reply not received - regState_Registered = 3, // update sent, reply received - regState_DeregPending = 4, // dereg sent, reply not received - regState_DeregDeferred = 5, // dereg requested while in Pending state - send dereg AFTER registration is confirmed - regState_Unregistered = 8, // not in any list - regState_Refresh = 9, // outstanding refresh (or target change) message - regState_NATMap = 10, // establishing NAT port mapping (service registrations only) - regState_UpdatePending = 11, // update in flight as result of mDNS_Update call - regState_NoTarget = 12, // service registration pending registration of hostname (ServiceRegistrations only) - regState_ExtraQueued = 13, // extra record to be registered upon completion of service registration (RecordRegistrations only) - regState_NATError = 14 // unable to complete NAT traversal + regState_Pending = 1, // update sent, reply not received + regState_Registered = 2, // update sent, reply received + regState_DeregPending = 3, // dereg sent, reply not received + regState_Unregistered = 4, // not in any list + regState_Refresh = 5, // outstanding refresh (or target change) message + regState_NATMap = 6, // establishing NAT port mapping + regState_UpdatePending = 7, // update in flight as result of mDNS_Update call + regState_NoTarget = 8, // SRV Record registration pending registration of hostname + regState_NATError = 9 // unable to complete NAT traversal } regState_t; enum @@ -1934,6 +1105,12 @@ enum Target_AutoHostAndNATMAP = 2 }; +typedef enum + { + mergeState_Zero = 0, + mergeState_DontMerge = 1 // Set on fatal error conditions to disable merging + } mergeState_t; + struct AuthRecord_struct { // For examples of how to set up this structure for use in mDNS_Register(), @@ -1956,11 +1133,10 @@ struct AuthRecord_struct mDNSu8 AllowRemoteQuery; // Set if we allow hosts not on the local link to query this record mDNSu8 ForceMCast; // Set by client to advertise solely via multicast, even for apparently unicast names - OwnerOptData WakeUp; // Fpr Sleep Proxy records, MAC address of original owner (so we can wake it) + OwnerOptData WakeUp; // WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record mDNSAddr AddressProxy; // For reverse-mapping Sleep Proxy PTR records, address in question mDNSs32 TimeRcvd; // In platform time units mDNSs32 TimeExpire; // In platform time units - // Field Group 3: Transient state for Authoritative Records mDNSu8 Acknowledged; // Set if we've given the success callback to the client @@ -2003,15 +1179,17 @@ struct AuthRecord_struct mDNSBool Private; // If zone is private, DNS updates may have to be encrypted to prevent eavesdropping mDNSOpaque16 updateid; // Identifier to match update request and response -- also used when transferring records to Sleep Proxy const domainname *zone; // the zone that is updated - mDNSAddr UpdateServer; // DNS server that handles updates for this zone - mDNSIPPort UpdatePort; // port on which server accepts dynamic updates - // !!!KRS not technically correct to cache longer than TTL - // SDC Perhaps should keep a reference to the relevant SRV record in the cache? ZoneData *nta; struct tcpInfo_t *tcp; + NATTraversalInfo NATinfo; + mDNSBool SRVChanged; // temporarily deregistered service because its SRV target or port changed + mergeState_t mState; // Unicast Record Registrations merge state + mDNSu8 refreshCount; // Number of refreshes to the server + mStatus updateError; // Record update resulted in Error ? // uDNS_UpdateRecord support fields // Do we really need all these in *addition* to NewRData and newrdlength above? + void *UpdateContext; // Context parameter for the update callback function mDNSu16 OrigRDLen; // previously registered, being deleted mDNSu16 InFlightRDLen; // currently being registered mDNSu16 QueuedRDLen; // pending operation (re-transmitting if necessary) THEN register the queued update @@ -2027,8 +1205,27 @@ struct AuthRecord_struct // DO NOT ADD ANY MORE FIELDS HERE }; +// IsLocalDomain alone is not sufficient to determine that a record is mDNS or uDNS. By default domain names within +// the "local" pseudo-TLD (and within the IPv4 and IPv6 link-local reverse mapping domains) are automatically treated +// as mDNS records, but it is also possible to force any record (even those not within one of the inherently local +// domains) to be handled as an mDNS record by setting the ForceMCast flag, or by setting a non-zero InterfaceID. +// For example, the reverse-mapping PTR record created in AdvertiseInterface sets the ForceMCast flag, since it points to +// a dot-local hostname, and therefore it would make no sense to register this record with a wide-area Unicast DNS server. +// The same applies to Sleep Proxy records, which we will answer for when queried via mDNS, but we never want to try +// to register them with a wide-area Unicast DNS server -- and we probably don't have the required credentials anyway. +// Currently we have no concept of a wide-area uDNS record scoped to a particular interface, so if the InterfaceID is +// nonzero we treat this the same as ForceMCast. +// Note: Question_uDNS(Q) is used in *only* one place -- on entry to mDNS_StartQuery_internal, to decide whether to set TargetQID. +// Everywhere else in the code, the determination of whether a question is unicast is made by checking to see if TargetQID is nonzero. #define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name)) -#define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Any && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname)) +#define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || \ + ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))) + +// Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address +// is not available locally for A or AAAA question respectively +#define QuerySuppressed(Q) ((Q)->SuppressUnusable && (Q)->SuppressQuery) + +#define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel) // Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field typedef struct ARListElem @@ -2059,7 +1256,7 @@ struct CacheRecord_struct mDNSs32 DelayDelivery; // Set if we want to defer delivery of this answer to local clients mDNSs32 NextRequiredQuery; // In platform time units mDNSs32 LastUsed; // In platform time units - DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer + DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer. Can never point to a NewQuestion. mDNSu32 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries #if ENABLE_MULTI_PACKET_QUERY_SNOOPING @@ -2096,33 +1293,6 @@ typedef struct HostnameInfo const void *StatusContext; // Client Context } HostnameInfo; -enum - { - DNSServer_Untested = 0, - DNSServer_Passed = 1, - DNSServer_Failed = 2, - DNSServer_Disabled = 3 - }; - -enum - { - DNSServer_FlagDelete = 1, - DNSServer_FlagNew = 2 - }; - -typedef struct DNSServer - { - struct DNSServer *next; - mDNSInterfaceID interface; // For specialized uses; we can have DNS servers reachable over specific interfaces - mDNSAddr addr; - mDNSIPPort port; - mDNSOpaque16 testid; - mDNSu32 flags; // Set when we're planning to delete this from the list - mDNSu32 teststate; // Have we sent bug-detection query to this server? - mDNSs32 lasttest; // Time we sent last bug-detection query to this server - domainname domain; // name->server matching for "split dns" - } DNSServer; - typedef struct ExtraResourceRecord_struct ExtraResourceRecord; struct ExtraResourceRecord_struct { @@ -2137,49 +1307,21 @@ struct ExtraResourceRecord_struct // Note: Within an mDNSServiceCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result); -// A ServiceRecordSet is basically a convenience structure to group together -// the PTR/SRV/TXT records that make up a standard service registration -// It contains its own ServiceCallback+ServiceContext to report aggregate results up to the next layer of software above +// A ServiceRecordSet has no special meaning to the core code of the Multicast DNS protocol engine; +// it is just a convenience structure to group together the records that make up a standard service +// registration so that they can be allocted and deallocted together as a single memory object. +// It contains its own ServiceCallback+ServiceContext to report aggregate results up to the next layer of software above. // It also contains: +// * the basic PTR/SRV/TXT triplet used to represent any DNS-SD service // * the "_services" PTR record for service enumeration -// * the optional target host name (for proxy registrations) // * the optional list of SubType PTR records // * the optional list of additional records attached to the service set (e.g. iChat pictures) -// -// ... and a bunch of stuff related to uDNS, some of which could be simplified or eliminated struct ServiceRecordSet_struct { // These internal state fields are used internally by mDNSCore; the client layer needn't be concerned with them. // No fields need to be set up by the client prior to calling mDNS_RegisterService(); // all required data is passed as parameters to that function. - - // Begin uDNS info **************** - // All of these fields should be eliminated - - // Note: The current uDNS code keeps an explicit list of registered services, and handles them - // differently to how individual records are treated (this is probably a mistake). What this means is - // that ServiceRecordSets for uDNS are kept in a linked list, whereas ServiceRecordSets for mDNS exist - // just as a convenient placeholder to group the component records together and are not kept on any list. - ServiceRecordSet *uDNS_next; - regState_t state; - mDNSBool srs_uselease; // dynamic update contains (should contain) lease option - mDNSBool TestForSelfConflict; // on name conflict, check if we're just seeing our own orphaned records - mDNSBool Private; // If zone is private, DNS updates may have to be encrypted to prevent eavesdropping - ZoneData *srs_nta; - mDNSOpaque16 id; - domainname zone; // the zone that is updated - mDNSAddr SRSUpdateServer; // primary name server for the record's zone !!!KRS not technically correct to cache longer than TTL - mDNSIPPort SRSUpdatePort; // port on which server accepts dynamic updates - NATTraversalInfo NATinfo; - mDNSBool ClientCallbackDeferred; // invoke client callback on completion of pending operation(s) - mStatus DeferredStatus; // status to deliver when above flag is set - mDNSBool SRVUpdateDeferred; // do we need to change target or port once current operation completes? - mDNSBool SRVChanged; // temporarily deregistered service because its SRV target or port changed - struct tcpInfo_t *tcp; - - // End uDNS info **************** - mDNSServiceCallback *ServiceCallback; void *ServiceContext; mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict @@ -2202,7 +1344,7 @@ struct ServiceRecordSet_struct #endif // We record the last eight instances of each duplicate query -// This gives us v4/v6 on each of Ethernet/AirPort and Firewire, and two free slots "for future expansion" +// This gives us v4/v6 on each of Ethernet, AirPort and Firewire, and two free slots "for future expansion" // If the host has more active interfaces that this it is not fatal -- duplicate question suppression will degrade gracefully. // Since we will still remember the last eight, the busiest interfaces will still get the effective duplicate question suppression. #define DupSuppressInfoSize 8 @@ -2254,7 +1396,8 @@ enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 }; #define AutoTunnelUnregistered(X) ( \ (X)->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered && \ (X)->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnelService. resrec.RecordType == kDNSRecordTypeUnregistered ) + (X)->AutoTunnelService. resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnel6Record. resrec.RecordType == kDNSRecordTypeUnregistered ) // Internal data structure to maintain authentication information typedef struct DomainAuthInfo @@ -2266,6 +1409,7 @@ typedef struct DomainAuthInfo AuthRecord AutoTunnelTarget; // Opaque hostname of tunnel endpoint; used as SRV target for AutoTunnelService record AuthRecord AutoTunnelDeviceInfo; // Device info of tunnel endpoint AuthRecord AutoTunnelService; // Service record (possibly NAT-Mapped) of IKE daemon implementing tunnel endpoint + AuthRecord AutoTunnel6Record; // AutoTunnel AAAA Record obtained from Connectivityd NATTraversalInfo AutoTunnelNAT; domainname domain; domainname keyname; @@ -2277,6 +1421,11 @@ typedef struct DomainAuthInfo // Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() typedef enum { QC_rmv = 0, QC_add = 1, QC_addnocache = 2 } QC_result; typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); + +#define NextQSendTime(Q) ((Q)->LastQTime + (Q)->ThisQInterval) +#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) +#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - NextQSendTime(Q) >= 0) + struct DNSQuestion_struct { // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. @@ -2305,23 +1454,33 @@ struct DNSQuestion_struct mDNSu32 RequestUnicast; // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces mDNSu32 CNAMEReferrals; // Count of how many CNAME redirections we've done + mDNSBool SuppressQuery; // This query should be suppressed and not sent on the wire // Wide Area fields. These are used internally by the uDNS core UDPSocket *LocalSocket; + mDNSBool deliverAddEvents; // Change in DNSSserver requiring to deliver ADD events DNSServer *qDNSServer; // Caching server for this query (in the absence of an SRV saying otherwise) + mDNSOpaque64 validDNSServers; // Valid DNSServers for this question + mDNSu16 noServerResponse; // At least one server did not respond. + mDNSu16 triedAllServersOnce; // Tried all DNS servers once mDNSu8 unansweredQueries;// The number of unanswered queries to this server ZoneData *nta; // Used for getting zone data for private or LLQ query mDNSAddr servAddr; // Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query mDNSIPPort servPort; struct tcpInfo_t *tcp; + mDNSIPPort tcpSrcPort; // Local Port TCP packet received on;need this as tcp struct is disposed + // by tcpCallback before calling into mDNSCoreReceive mDNSu8 NoAnswer; // Set if we want to suppress answers until tunnel setup has completed // LLQ-specific fields. These fields are only meaningful when LongLived flag is set LLQ_State state; mDNSu32 ReqLease; // seconds (relative) mDNSs32 expire; // ticks (absolute) - mDNSs16 ntries; + mDNSs16 ntries; // for UDP: the number of packets sent for this LLQ state + // for TCP: there is some ambiguity in the use of this variable, but in general, it is + // the number of TCP/TLS connection attempts for this LLQ state, or + // the number of packets sent for this TCP/TLS connection mDNSOpaque64 id; // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() @@ -2336,6 +1495,7 @@ struct DNSQuestion_struct mDNSBool ExpectUnique; // Set by client if it's expecting unique RR(s) for this question, not shared RRs mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names mDNSBool ReturnIntermed; // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results + mDNSBool SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire mDNSQuestionCallback *QuestionCallback; void *QuestionContext; }; @@ -2407,6 +1567,12 @@ typedef struct DNameListElem } DNameListElem; #if APPLE_OSX_mDNSResponder +// Different states that we go through locating the peer +#define TC_STATE_AAAA_PEER 0x000000001 /* Peer's BTMM IPv6 address */ +#define TC_STATE_AAAA_PEER_RELAY 0x000000002 /* Peer's IPv6 Relay address */ +#define TC_STATE_SRV_PEER 0x000000003 /* Peer's SRV Record corresponding to IPv4 address */ +#define TC_STATE_ADDR_PEER 0x000000004 /* Peer's IPv4 address */ + typedef struct ClientTunnel { struct ClientTunnel *next; @@ -2414,9 +1580,12 @@ typedef struct ClientTunnel mDNSBool MarkedForDeletion; mDNSv6Addr loc_inner; mDNSv4Addr loc_outer; + mDNSv6Addr loc_outer6; mDNSv6Addr rmt_inner; mDNSv4Addr rmt_outer; + mDNSv6Addr rmt_outer6; mDNSIPPort rmt_outer_port; + mDNSu16 tc_state; DNSQuestion q; } ClientTunnel; #endif @@ -2480,6 +1649,8 @@ typedef struct SearchListElem DNSQuestion AutomaticBrowseQ; DNSQuestion RegisterQ; DNSQuestion DefRegisterQ; + DNSQuestion DirQ; + int numDirAnswers; ARListElem *AuthRecs; } SearchListElem; @@ -2500,10 +1671,11 @@ typedef void mDNSCallback(mDNS *const m, mStatus result); #define CACHE_HASH_SLOTS 499 -enum +enum // Bit flags -- i.e. values should be 1, 2, 4, 8, etc. { mDNS_KnownBug_PhantomInterfaces = 1, - mDNS_KnownBug_LossySyslog = 2 // <rdar://problem/6561888> + mDNS_KnownBug_LimitedIPv6 = 2, + mDNS_KnownBug_LossySyslog = 4 // <rdar://problem/6561888> }; enum @@ -2549,7 +1721,7 @@ struct mDNS_struct mDNSs32 timenow_last; // The time the last time we ran mDNSs32 NextScheduledEvent; // Derived from values below mDNSs32 ShutdownTime; // Set when we're shutting down; allows us to skip some unnecessary steps - mDNSs32 SuppressSending; // Don't send *any* packets during this time + mDNSs32 SuppressSending; // Don't send local-link mDNS packets during this time mDNSs32 NextCacheCheck; // Next time to refresh cache record before it expires mDNSs32 NextScheduledQuery; // Next time to send query in its exponential backoff sequence mDNSs32 NextScheduledProbe; // Next time to probe for new authoritative record @@ -2559,26 +1731,32 @@ struct mDNS_struct mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire mDNSu32 RandomReconfirmDelay; // For de-synchronization of reconfirmation queries on the wire mDNSs32 PktNum; // Unique sequence number assigned to each received packet + mDNSu8 LocalRemoveEvents; // Set if we may need to deliver remove events for local-only questions and/or local-only records mDNSu8 SleepState; // Set if we're sleeping mDNSu8 SleepSeqNum; // "Epoch number" of our current period of wakefulness mDNSu8 SystemWakeOnLANEnabled; // Set if we want to register with a Sleep Proxy before going to sleep + mDNSu8 SentSleepProxyRegistration;// Set if we registered (or tried to register) with a Sleep Proxy + mDNSu8 SystemSleepOnlyIfWakeOnLAN;// Set if we may only sleep if we managed to register with a Sleep Proxy + mDNSs32 AnnounceOwner; // After waking from sleep, include OWNER option in packets until this time mDNSs32 DelaySleep; // To inhibit re-sleeping too quickly right after wake mDNSs32 SleepLimit; // Time window to allow deregistrations, etc., // during which underying platform layer should inhibit system sleep - mDNSs32 NextScheduledSPRetry; // Time next sleep proxy registration action is required. Only valid if SleepLimit is nonzero. + mDNSs32 NextScheduledSPRetry; // Time next sleep proxy registration action is required. + // Only valid if SleepLimit is nonzero and DelaySleep is zero. // These fields only required for mDNS Searcher... DNSQuestion *Questions; // List of all registered questions, active and inactive DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() - DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly - DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only questions not yet answered + DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly or mDNSInterface_P2P + DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only or P2P questions not yet answered mDNSu32 rrcache_size; // Total number of available cache entries mDNSu32 rrcache_totalused; // Number of cache entries currently occupied mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions mDNSu32 rrcache_report; CacheEntity *rrcache_free; CacheGroup *rrcache_hash[CACHE_HASH_SLOTS]; + mDNSs32 rrcache_nextcheck[CACHE_HASH_SLOTS]; // Fields below only required for mDNS Responder... domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8 @@ -2589,7 +1767,7 @@ struct mDNS_struct AuthRecord DeviceInfo; AuthRecord *ResourceRecords; AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records - AuthRecord *NewLocalRecords; // Fresh local-only records not yet delivered to local-only questions + AuthRecord *NewLocalRecords; // Fresh AuthRecords (both local-only and public) not yet delivered to our local-only questions AuthRecord *CurrentRecord; // Next AuthRecord about to be examined NetworkInterfaceInfo *HostInterfaces; mDNSs32 ProbeFailTime; @@ -2599,9 +1777,7 @@ struct mDNS_struct // Unicast-specific data mDNSs32 NextuDNSEvent; // uDNS next event mDNSs32 NextSRVUpdate; // Time to perform delayed update - mDNSs32 SuppressStdPort53Queries; // Wait before allowing the next standard unicast query to the user's configured DNS server - ServiceRecordSet *ServiceRegistrations; DNSServer *DNSServers; // list of DNS servers mDNSAddr Router; @@ -2617,9 +1793,11 @@ struct mDNS_struct HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata mDNSv6Addr AutoTunnelHostAddr; // IPv6 address advertised for AutoTunnel services on this machine mDNSBool AutoTunnelHostAddrActive; + mDNSv6Addr AutoTunnelRelayAddr; // IPv6 address advertised for AutoTunnel Relay services on this machine domainlabel AutoTunnelLabel; // Used to construct hostname for *IPv4* address of tunnel endpoints mDNSBool RegisterSearchDomains; + mDNSBool RegisterAutoTunnel6; // NAT-Traversal fields NATTraversalInfo LLQNAT; // Single shared NAT Traversal to receive inbound LLQ notifications @@ -2664,6 +1842,7 @@ struct mDNS_struct #if APPLE_OSX_mDNSResponder ClientTunnel *TunnelClients; uuid_t asl_uuid; // uuid for ASL logging + void *WCF; #endif // Fixed storage, to avoid creating large objects on the stack @@ -2684,25 +1863,17 @@ struct mDNS_struct #pragma mark - Useful Static Constants #endif -extern const mDNSIPPort zeroIPPort; -extern const mDNSv4Addr zerov4Addr; -extern const mDNSv6Addr zerov6Addr; -extern const mDNSEthAddr zeroEthAddr; -extern const mDNSv4Addr onesIPv4Addr; -extern const mDNSv6Addr onesIPv6Addr; -extern const mDNSEthAddr onesEthAddr; -extern const mDNSAddr zeroAddr; - -extern const OwnerOptData zeroOwner; - extern const mDNSInterfaceID mDNSInterface_Any; // Zero extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value extern const mDNSInterfaceID mDNSInterface_Unicast; // Special value +extern const mDNSInterfaceID mDNSInterfaceMark; // Special value +extern const mDNSInterfaceID mDNSInterface_P2P; // Special value extern const mDNSIPPort DiscardPort; extern const mDNSIPPort SSHPort; extern const mDNSIPPort UnicastDNSPort; extern const mDNSIPPort SSDPPort; +extern const mDNSIPPort IPSECPort; extern const mDNSIPPort NSIPCPort; extern const mDNSIPPort NATPMPAnnouncementPort; extern const mDNSIPPort NATPMPPort; @@ -2711,8 +1882,22 @@ extern const mDNSIPPort MulticastDNSPort; extern const mDNSIPPort LoopbackIPCPort; extern const mDNSIPPort PrivateDNSPort; +extern const OwnerOptData zeroOwner; + +extern const mDNSIPPort zeroIPPort; +extern const mDNSv4Addr zerov4Addr; +extern const mDNSv6Addr zerov6Addr; +extern const mDNSEthAddr zeroEthAddr; +extern const mDNSv4Addr onesIPv4Addr; +extern const mDNSv6Addr onesIPv6Addr; +extern const mDNSEthAddr onesEthAddr; +extern const mDNSAddr zeroAddr; + extern const mDNSv4Addr AllDNSAdminGroup; -extern const mDNSv4Addr AllSystemsMcast; +extern const mDNSv4Addr AllHosts_v4; +extern const mDNSv6Addr AllHosts_v6; +extern const mDNSv6Addr NDP_prefix; +extern const mDNSEthAddr AllHosts_v6_Eth; extern const mDNSAddr AllDNSLinkGroup_v4; extern const mDNSAddr AllDNSLinkGroup_v6; @@ -2726,6 +1911,9 @@ extern const mDNSOpaque16 UpdateRespFlags; extern const mDNSOpaque64 zeroOpaque64; +extern mDNSBool StrictUnicastOrdering; +extern mDNSu8 NumUnicastDNSServers; + #define localdomain (*(const domainname *)"\x5" "local") #define DeviceInfoName (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp") #define SleepProxyServiceType (*(const domainname *)"\xC" "_sleep-proxy" "\x4" "_udp") @@ -2845,7 +2033,7 @@ extern void mDNS_GrowCache (mDNS *const m, CacheEntity *storage, mDNSu32 numr extern void mDNS_StartExit (mDNS *const m); extern void mDNS_FinalExit (mDNS *const m); #define mDNS_Close(m) do { mDNS_StartExit(m); mDNS_FinalExit(m); } while(0) -#define mDNS_ExitNow(m, now) ((now) - (m)->ShutdownTime >= 0 || (!(m)->ResourceRecords && !(m)->ServiceRegistrations)) +#define mDNS_ExitNow(m, now) ((now) - (m)->ShutdownTime >= 0 || (!(m)->ResourceRecords)) extern mDNSs32 mDNS_Execute (mDNS *const m); @@ -2868,6 +2056,8 @@ extern mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *travers extern DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name); +extern void mDNS_UpdateAllowSleep(mDNS *const m); + // *************************************************************************** #if 0 #pragma mark - @@ -2882,6 +2072,12 @@ extern mDNSs32 mDNSPlatformOneSecond; #pragma mark - General utility and helper functions #endif +// mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal +// mDNS_Dereg_rapid is used to send one goodbye instead of three, when we want the memory available for reuse sooner +// mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict +// mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered +typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type; + // mDNS_RegisterService is a single call to register the set of resource records associated with a given named service. // // mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery, @@ -2915,7 +2111,8 @@ extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl); extern mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context); extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname); -extern mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr); +extern mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt); +#define mDNS_DeregisterService(M,S) mDNS_DeregisterService_drt((M), (S), mDNS_Dereg_normal) extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, const domainlabel *const name, const domainname *const type, const domainname *const domain, @@ -2954,8 +2151,11 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT #define mDNS_StopAdvertiseDomains mDNS_Deregister extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m); - -extern DNSServer *GetServerForName(mDNS *m, const domainname *name); +extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr); + +extern DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID); +extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question); +extern void SetValidDNSServers(mDNS *m, DNSQuestion *question); // *************************************************************************** #if 0 @@ -2980,10 +2180,15 @@ extern DNSServer *GetServerForName(mDNS *m, const domainname *name); extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); extern mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2); +typedef mDNSBool DomainNameComparisonFn(const domainname *const d1, const domainname *const d2); extern mDNSBool IsLocalDomain(const domainname *d); // returns true for domains that by default should be looked up using link-local multicast +#define StripFirstLabel(X) ((const domainname *)&(X)->c[(X)->c[0] ? 1 + (X)->c[0] : 0]) + #define FirstLabel(X) ((const domainlabel *)(X)) -#define SecondLabel(X) ((const domainlabel *)&(X)->c[1 + (X)->c[0]]) +#define SecondLabel(X) ((const domainlabel *)StripFirstLabel(X)) +#define ThirdLabel(X) ((const domainlabel *)StripFirstLabel(StripFirstLabel(X))) + extern const mDNSu8 *LastLabel(const domainname *d); // Get total length of domain name, in native DNS format, including terminal root label @@ -3109,6 +2314,12 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr); // returns true for RFC1 ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLinkLocal(&(X)->ip.v4) : \ ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLinkLocal(&(X)->ip.v6) : mDNSfalse) +#define mDNSv4AddressIsLoopback(X) ((X)->b[0] == 127 && (X)->b[1] == 0 && (X)->b[2] == 0 && (X)->b[3] == 1) +#define mDNSv6AddressIsLoopback(X) ((((X)->l[0] | (X)->l[1] | (X)->l[2]) == 0) && ((X)->b[12] == 0 && (X)->b[13] == 0 && (X)->b[14] == 0 && (X)->b[15] == 1)) + +#define mDNSAddressIsLoopback(X) ( \ + ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLoopback(&(X)->ip.v4) : \ + ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLoopback(&(X)->ip.v6) : mDNSfalse) // *************************************************************************** #if 0 #pragma mark - @@ -3149,8 +2360,8 @@ extern void RecreateNATMappings(mDNS *const m); extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext); extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn); extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router); -extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port); -extern void PushDNSServerToEnd(mDNS *const m, DNSQuestion *q); +extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped); +extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q); extern void mDNS_AddSearchDomain(const domainname *const domain); // We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2 @@ -3299,8 +2510,8 @@ typedef void (*TCPConnectionCallback)(TCPSocket *sock, void *context, mDNSBool C extern TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port); // creates a TCP socket extern TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd); extern int mDNSPlatformTCPGetFD(TCPSocket *sock); -extern mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, - TCPConnectionCallback callback, void *context); +extern mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, + mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context); extern void mDNSPlatformTCPCloseConnection(TCPSocket *sock); extern long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed); extern long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len); @@ -3309,7 +2520,7 @@ extern void mDNSPlatformUDPClose(UDPSocket *sock); extern void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd); extern void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID); extern void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID); -extern void mDNSPlatformSetLocalARP(const mDNSv4Addr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID); +extern void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID); extern void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst); // mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd @@ -3323,10 +2534,12 @@ extern void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, m extern mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router); extern void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status); +extern void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep); + #ifdef _LEGACY_NAT_TRAVERSAL_ // Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core. extern void LNT_SendDiscoveryMsg(mDNS *m); -extern void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *data, mDNSu16 len); +extern void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len); extern mStatus LNT_GetExternalAddress(mDNS *m); extern mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *n); extern mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *n); @@ -3376,41 +2589,173 @@ extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); extern void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, - const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); + const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); extern void mDNSCoreRestartQueries(mDNS *const m); extern mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m); extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); -extern mDNSBool mDNSCoreReadyForSleep(mDNS *m); +extern mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now); extern mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now); -extern void mDNSCoreBeSleepProxyServer(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower); extern void mDNSCoreReceiveRawPacket (mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID); extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); -extern CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg); +extern CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay); +extern void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event); extern void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease); extern void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, - const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID); + const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, + mDNSInterfaceID InterfaceID, DNSServer *dnsserver); extern void CompleteDeregistration(mDNS *const m, AuthRecord *rr); -extern void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const CacheRecord *sps[3]); -#define PrototypeSPSName(X) ((X)[0] >= 11 && (X)[3] == '-' && (X)[ 4] == '9' && (X)[ 5] == '9' && \ - (X)[6] == '-' && (X)[ 7] == '9' && (X)[ 8] == '9' && \ - (X)[9] == '-' && (X)[10] == '9' && (X)[11] == '9' ) -#define ValidSPSName(X) ((X)[0] >= 5 && mDNSIsDigit((X)[1]) && mDNSIsDigit((X)[2]) && mDNSIsDigit((X)[4]) && mDNSIsDigit((X)[5])) -#define SPSMetric(X) (!ValidSPSName(X) || PrototypeSPSName(X) ? 1000000 : \ - ((X)[1]-'0') * 100000 + ((X)[2]-'0') * 10000 + ((X)[4]-'0') * 1000 + ((X)[5]-'0') * 100 + ((X)[7]-'0') * 10 + ((X)[8]-'0')) extern void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord); +extern char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID); +extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *new); +extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr); +extern void CheckSuppressUnusableQuestions(mDNS *const m); // For now this AutoTunnel stuff is specific to Mac OS X. // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer #if APPLE_OSX_mDNSResponder extern void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); extern void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q); -extern void SetupLocalAutoTunnelInterface_internal(mDNS *const m); +extern void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool servicesStarting); extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m); +extern mStatus ActivateLocalProxy(mDNS *const m, char *ifname); +extern void RemoveAutoTunnel6Record(mDNS *const m); +extern void SetupConndConfigChanges(mDNS *const m); +extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); +#endif + +// *************************************************************************** +#if 0 +#pragma mark - +#pragma mark - Sleep Proxy #endif +// Sleep Proxy Server Property Encoding +// +// Sleep Proxy Servers are advertised using a structured service name, consisting of four +// metrics followed by a human-readable name. The metrics assist clients in deciding which +// Sleep Proxy Server(s) to use when multiple are available on the network. Each metric +// is a two-digit decimal number in the range 10-99. Lower metrics are generally better. +// +// AA-BB-CC-DD Name +// +// Metrics: +// +// AA = Intent +// BB = Portability +// CC = Marginal Power +// DD = Total Power +// +// +// ** Intent Metric ** +// +// 20 = Dedicated Sleep Proxy Server -- a device, permanently powered on, +// installed for the express purpose of providing Sleep Proxy Service. +// +// 30 = Primary Network Infrastructure Hardware -- a router, DHCP server, NAT gateway, +// or similar permanently installed device which is permanently powered on. +// This is hardware designed for the express purpose of being network +// infrastructure, and for most home users is typically a single point +// of failure for the local network -- e.g. most home users only have +// a single NAT gateway / DHCP server. Even though in principle the +// hardware might technically be capable of running different software, +// a typical user is unlikely to do that. e.g. AirPort base station. +// +// 40 = Primary Network Infrastructure Software -- a general-purpose computer +// (e.g. Mac, Windows, Linux, etc.) which is currently running DHCP server +// or NAT gateway software, but the user could choose to turn that off +// fairly easily. e.g. iMac running Internet Sharing +// +// 50 = Secondary Network Infrastructure Hardware -- like primary infrastructure +// hardware, except not a single point of failure for the entire local network. +// For example, an AirPort base station in bridge mode. This may have clients +// associated with it, and if it goes away those clients will be inconvenienced, +// but unlike the NAT gateway / DHCP server, the entire local network is not +// dependent on it. +// +// 60 = Secondary Network Infrastructure Software -- like 50, but in a general- +// purpose CPU. +// +// 70 = Incidentally Available Hardware -- a device which has no power switch +// and is generally left powered on all the time. Even though it is not a +// part of what we conventionally consider network infrastructure (router, +// DHCP, NAT, DNS, etc.), and the rest of the network can operate fine +// without it, since it's available and unlikely to be turned off, it is a +// reasonable candidate for providing Sleep Proxy Service e.g. Apple TV, +// or an AirPort base station in client mode, associated with an existing +// wireless network (e.g. AirPort Express connected to a music system, or +// being used to share a USB printer). +// +// 80 = Incidentally Available Software -- a general-purpose computer which +// happens at this time to be set to "never sleep", and as such could be +// useful as a Sleep Proxy Server, but has not been intentionally provided +// for this purpose. Of all the Intent Metric categories this is the +// one most likely to be shut down or put to sleep without warning. +// However, if nothing else is availalable, it may be better than nothing. +// e.g. Office computer in the workplace which has been set to "never sleep" +// +// +// ** Portability Metric ** +// +// Inversely related to mass of device, on the basis that, all other things +// being equal, heavier devices are less likely to be moved than lighter devices. +// E.g. A MacBook running Internet Sharing is probably more likely to be +// put to sleep and taken away than a Mac Pro running Internet Sharing. +// The Portability Metric is a logarithmic decibel scale, computed by taking the +// (approximate) mass of the device in milligrammes, taking the base 10 logarithm +// of that, multiplying by 10, and subtracting the result from 100: +// +// Portability Metric = 100 - (log10(mg) * 10) +// +// The Portability Metric is not necessarily computed literally from the actual +// mass of the device; the intent is just that lower numbers indicate more +// permanent devices, and higher numbers indicate devices more likely to be +// removed from the network, e.g., in order of increasing portability: +// +// Mac Pro < iMac < Laptop < iPhone +// +// Example values: +// +// 10 = 1 metric tonne +// 40 = 1kg +// 70 = 1g +// 90 = 10mg +// +// +// ** Marginal Power and Total Power Metrics ** +// +// The Marginal Power Metric is the power difference between sleeping and staying awake +// to be a Sleep Proxy Server. +// +// The Total Power Metric is the total power consumption when being Sleep Proxy Server. +// +// The Power Metrics use a logarithmic decibel scale, computed as ten times the +// base 10 logarithm of the (approximate) power in microwatts: +// +// Power Metric = log10(uW) * 10 +// +// Higher values indicate higher power consumption. Example values: +// +// 10 = 10 uW +// 20 = 100 uW +// 30 = 1 mW +// 60 = 1 W +// 90 = 1 kW + +extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower); +#define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP) \ + do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP)); mDNS_Unlock(m); } while(0) + +extern void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const CacheRecord *sps[3]); +#define PrototypeSPSName(X) ((X)[0] >= 11 && (X)[3] == '-' && (X)[ 4] == '9' && (X)[ 5] == '9' && \ + (X)[6] == '-' && (X)[ 7] == '9' && (X)[ 8] == '9' && \ + (X)[9] == '-' && (X)[10] == '9' && (X)[11] == '9' ) +#define ValidSPSName(X) ((X)[0] >= 5 && mDNSIsDigit((X)[1]) && mDNSIsDigit((X)[2]) && mDNSIsDigit((X)[4]) && mDNSIsDigit((X)[5])) +#define SPSMetric(X) (!ValidSPSName(X) || PrototypeSPSName(X) ? 1000000 : \ + ((X)[1]-'0') * 100000 + ((X)[2]-'0') * 10000 + ((X)[4]-'0') * 1000 + ((X)[5]-'0') * 100 + ((X)[7]-'0') * 10 + ((X)[8]-'0')) + // *************************************************************************** #if 0 #pragma mark - @@ -3446,29 +2791,30 @@ struct CompileTimeAssertionChecks_mDNS char assertG[(sizeof(ARP_EthIP ) == 28 ) ? 1 : -1]; char assertH[(sizeof(IPv4Header ) == 20 ) ? 1 : -1]; char assertI[(sizeof(IPv6Header ) == 40 ) ? 1 : -1]; - char assertJ[(sizeof(IPv6ND ) == 24 ) ? 1 : -1]; + char assertJ[(sizeof(IPv6NDP ) == 24 ) ? 1 : -1]; char assertK[(sizeof(UDPHeader ) == 8 ) ? 1 : -1]; - char assertL[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; + char assertL[(sizeof(IKEHeader ) == 28 ) ? 1 : -1]; + char assertM[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; // Check our structures are reasonable sizes. Including overly-large buffers, or embedding // other overly-large structures instead of having a pointer to them, can inadvertently // cause structure sizes (and therefore memory usage) to balloon unreasonably. char sizecheck_RDataBody [(sizeof(RDataBody) == 264) ? 1 : -1]; - char sizecheck_ResourceRecord [(sizeof(ResourceRecord) <= 56) ? 1 : -1]; - char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1000) ? 1 : -1]; - char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 176) ? 1 : -1]; - char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 176) ? 1 : -1]; - char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 728) ? 1 : -1]; - char sizecheck_ZoneData [(sizeof(ZoneData) <= 1560) ? 1 : -1]; + char sizecheck_ResourceRecord [(sizeof(ResourceRecord) <= 64) ? 1 : -1]; + char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1]; + char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 184) ? 1 : -1]; + char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 184) ? 1 : -1]; + char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 752) ? 1 : -1]; + char sizecheck_ZoneData [(sizeof(ZoneData) <= 1588) ? 1 : -1]; char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 192) ? 1 : -1]; - char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 2800) ? 1 : -1]; - char sizecheck_DNSServer [(sizeof(DNSServer) <= 312) ? 1 : -1]; - char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 5968) ? 1 : -1]; + char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; + char sizecheck_DNSServer [(sizeof(DNSServer) <= 320) ? 1 : -1]; + char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6750) ? 1 : -1]; char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5500) ? 1 : -1]; - char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 5500) ? 1 : -1]; - char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 2944) ? 1 : -1]; + char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7550) ? 1 : -1]; + char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3050) ? 1 : -1]; #if APPLE_OSX_mDNSResponder - char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1072) ? 1 : -1]; + char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1104) ? 1 : -1]; #endif }; diff --git a/external/apache2/mDNSResponder/dist/mDNSPosix/PosixDaemon.c b/external/apache2/mDNSResponder/dist/mDNSPosix/PosixDaemon.c index 8a440129b1e..350063161f8 100644 --- a/external/apache2/mDNSResponder/dist/mDNSPosix/PosixDaemon.c +++ b/external/apache2/mDNSResponder/dist/mDNSPosix/PosixDaemon.c @@ -18,74 +18,15 @@ Contains: main & associated Application layer for mDNSResponder on Linux. - Change History (most recent first): + */ -Log: PosixDaemon.c,v $ -Revision 1.49 2009/04/30 20:07:51 mcguire -<rdar://problem/6822674> Support multiple UDSs from launchd - -Revision 1.48 2009/04/11 01:43:28 jessic2 -<rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically - -Revision 1.47 2009/01/11 03:20:06 mkrochma -<rdar://problem/5797526> Fixes from Igor Seleznev to get mdnsd working on Solaris - -Revision 1.46 2008/11/03 23:09:15 cheshire -Don't need to include mDNSDebug.h as well as mDNSEmbeddedAPI.h - -Revision 1.45 2008/10/03 18:25:17 cheshire -Instead of calling "m->MainCallback" function pointer directly, call mDNSCore routine "mDNS_ConfigChanged(m);" - -Revision 1.44 2008/09/15 23:52:30 cheshire -<rdar://problem/6218902> mDNSResponder-177 fails to compile on Linux with .desc pseudo-op -Made __crashreporter_info__ symbol conditional, so we only use it for OS X build - -Revision 1.43 2007/10/22 20:05:34 cheshire -Use mDNSPlatformSourceAddrForDest instead of FindSourceAddrForIP - -Revision 1.42 2007/09/18 19:09:02 cheshire -<rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings - -Revision 1.41 2007/09/04 17:02:25 cheshire -<rdar://problem/5458929> False positives in changed files list in nightly builds -Added MDNS_VERSIONSTR_NODTS option at the reqest of Rishi Srivatsavai (Sun) - -Revision 1.40 2007/07/31 23:08:34 mcguire -<rdar://problem/5329542> BTMM: Make AutoTunnel mode work with multihoming - -Revision 1.39 2007/03/21 00:30:44 cheshire -Remove obsolete mDNS_DeleteDNSServers() call - -Revision 1.38 2007/02/14 01:58:19 cheshire -<rdar://problem/4995831> Don't delete Unix Domain Socket on exit if we didn't create it on startup - -Revision 1.37 2007/02/07 19:32:00 cheshire -<rdar://problem/4980353> All mDNSResponder components should contain version strings in SCCS-compatible format - -Revision 1.36 2007/02/06 19:06:48 cheshire -<rdar://problem/3956518> Need to go native with launchd - -Revision 1.35 2007/01/05 08:30:52 cheshire -Trim excessive "Log" checkin history from before 2006 -(checkin history still available via "cvs log ..." of course) - -Revision 1.34 2007/01/05 05:46:08 cheshire -Add mDNS *const m parameter to udsserver_handle_configchange() - -Revision 1.33 2006/12/21 00:10:53 cheshire -Make mDNS_PlatformSupport PlatformStorage a static global instead of a stack variable - -Revision 1.32 2006/11/03 22:28:50 cheshire -PosixDaemon needs to handle mStatus_ConfigChanged and mStatus_GrowCache messages - -Revision 1.31 2006/08/14 23:24:46 cheshire -Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 - -Revision 1.30 2006/07/07 01:09:12 cheshire -<rdar://problem/4472013> Add Private DNS server functionality to dnsextd -Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsextd - -*/ +#if __APPLE__ +// In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated” +// error, which prevents compilation because we build with "-Werror". +// Since this is supposed to be portable cross-platform code, we don't care that daemon is +// deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message. +#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou +#endif #include <stdio.h> #include <string.h> @@ -97,6 +38,11 @@ Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsex #include <pwd.h> #include <sys/types.h> +#if __APPLE__ +#undef daemon +extern int daemon(int, int); +#endif + #include "mDNSEmbeddedAPI.h" #include "mDNSPosix.h" #include "mDNSUNP.h" // For daemon() @@ -335,16 +281,24 @@ int main(int argc, char **argv) // uds_daemon support //////////////////////////////////////////////////////////// -mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context) +mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data) /* Support routine for uds_daemon.c */ { // Depends on the fact that udsEventCallback == mDNSPosixEventCallback + (void) platform_data; return mDNSPosixAddFDToEventLoop(fd, callback, context); } -mStatus udsSupportRemoveFDFromEventLoop(int fd) // Note: This also CLOSES the file descriptor +int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data) + { + (void) platform_data; + return recv(fd, buf, len, flags); + } + +mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor { mStatus err = mDNSPosixRemoveFDFromEventLoop(fd); + (void) platform_data; close(fd); return err; } diff --git a/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSPosix.c b/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSPosix.c index e984ed0e0cb..a01a2544e14 100755 --- a/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSPosix.c +++ b/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSPosix.c @@ -26,123 +26,7 @@ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't * understand why variable y is not of type "char*" just proves the point that poor code * layout leads people to unfortunate misunderstandings about how the C language really works.) - - Change History (most recent first): - -Log: mDNSPosix.c,v $ -Revision 1.108 2009/01/25 03:16:46 mkrochma -Added skeleton definition of mDNSPlatformSetLocalARP - -Revision 1.107 2009/01/07 08:25:03 mkrochma -Added skeleton definition of mDNSPlatformUpdateProxyList - -Revision 1.106 2008/10/22 17:19:57 cheshire -Don't need to define BPF_fd any more (it's now per-interface, not global) - -Revision 1.105 2008/10/03 23:34:08 cheshire -Added skeleton definition of mDNSPlatformSendRawPacket - -Revision 1.104 2008/09/05 22:16:48 cheshire -<rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning -Add "UDPSocket *src" parameter in mDNSPlatformSendUDP - -Revision 1.103 2007/10/02 19:31:17 cheshire -In ParseDNSServers, should use strncasecmp for case-insensitive compare - -Revision 1.102 2007/09/12 19:23:17 cheshire -Get rid of unnecessary mDNSPlatformTCPIsConnected() routine - -Revision 1.101 2007/07/20 00:54:23 cheshire -<rdar://problem/4641118> Need separate SCPreferences for per-user .Mac settings - -Revision 1.100 2007/07/19 21:45:30 cheshire -Fixed code spacing - -Revision 1.99 2007/07/11 02:56:51 cheshire -<rdar://problem/5303807> Register IPv6-only hostname and don't create port mappings for AutoTunnel services -Remove unused mDNSPlatformDefaultRegDomainChanged - -Revision 1.98 2007/06/20 01:10:13 cheshire -<rdar://problem/5280520> Sync iPhone changes into main mDNSResponder code - -Revision 1.97 2007/04/26 00:35:16 cheshire -<rdar://problem/5140339> uDNS: Domain discovery not working over VPN -Fixes to make sure results update correctly when connectivity changes (e.g. a DNS server -inside the firewall may give answers where a public one gives none, and vice versa.) - -Revision 1.96 2007/04/22 20:29:59 cheshire -Fix locking error - -Revision 1.95 2007/04/22 20:15:46 cheshire -Add missing parameters for mDNSPosixEventCallback - -Revision 1.94 2007/04/17 19:21:29 cheshire -<rdar://problem/5140339> Domain discovery not working over VPN - -Revision 1.93 2007/04/16 20:49:40 cheshire -Fix compile errors for mDNSPosix build - -Revision 1.92 2007/04/05 20:40:37 cheshire -Remove unused mDNSPlatformTCPGetFlags() - -Revision 1.91 2007/03/22 18:31:48 cheshire -Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy - -Revision 1.90 2007/03/21 00:31:45 cheshire -Remove unnecessary (and unimplemented) platform functions - -Revision 1.89 2007/03/20 17:07:15 cheshire -Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket" - -Revision 1.88 2007/03/07 00:30:18 mkrochma -<rdar://problem/5034370> POSIX: kDNSServiceInterfaceIndexAny not correctly handled -Thanks goes to Aidan Williams of Audinate who did a lot of work in diagnosing this - -Revision 1.87 2007/02/08 21:12:28 cheshire -<rdar://problem/4386497> Stop reading /etc/mDNSResponder.conf on every sleep/wake - -Revision 1.86 2007/01/05 08:30:52 cheshire -Trim excessive "Log" checkin history from before 2006 -(checkin history still available via "cvs log ..." of course) - -Revision 1.85 2007/01/04 23:12:20 cheshire -Remove unused mDNSPlatformDefaultBrowseDomainChanged - -Revision 1.84 2006/12/22 21:07:35 cheshire -<rdar://problem/4742742> Read *all* DNS keys from keychain, - not just key for the system-wide default registration domain - -Revision 1.83 2006/12/21 00:09:46 cheshire -Use mDNSPlatformMemZero instead of bzero - -Revision 1.82 2006/12/19 22:43:55 cheshire -Fix compiler warnings - -Revision 1.81 2006/08/14 23:24:46 cheshire -Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 - -Revision 1.80 2006/07/22 03:05:33 cheshire -Improve error reporting for socket creation failures - -Revision 1.79 2006/07/06 00:02:16 cheshire -<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder - -Revision 1.78 2006/06/28 09:12:22 cheshire -Added debugging message - -Revision 1.77 2006/03/19 02:00:11 cheshire -<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions - -Revision 1.76 2006/01/09 19:29:16 cheshire -<rdar://problem/4403128> Cap number of "sendto failed" messages we allow mDNSResponder to log - -Revision 1.75 2006/01/05 22:04:57 cheshire -<rdar://problem/4399479> Log error message when send fails with "operation not permitted" - -Revision 1.74 2006/01/05 21:45:27 cheshire -<rdar://problem/4400118> Fix uninitialized structure member in IPv6 code - -*/ + */ #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "DNSCommon.h" @@ -450,12 +334,13 @@ mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) return -1; } -mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, +mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context) { (void)sock; // Unused (void)dst; // Unused (void)dstport; // Unused + (void)hostname; // Unused (void)InterfaceID; // Unused (void)callback; // Unused (void)context; // Unused @@ -509,8 +394,9 @@ mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *c (void)InterfaceID; // Unused } -mDNSexport void mDNSPlatformSetLocalARP(const mDNSv4Addr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) +mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) { + (void)m; // Unused (void)tpa; // Unused (void)tha; // Unused (void)InterfaceID; // Unused @@ -596,7 +482,7 @@ mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) mDNSAddr DNSAddr; DNSAddr.type = mDNSAddrType_IPv4; DNSAddr.ip.v4.NotAnInteger = ina.s_addr; - mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort); + mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse); numOfServers++; } } @@ -627,6 +513,7 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const assert(m != NULL); if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); + if (index == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); if (index == kDNSServiceInterfaceIndexAny ) return(mDNSInterface_Any); intf = (PosixNetworkInterface*)(m->HostInterfaces); @@ -643,6 +530,7 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNS assert(m != NULL); if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); + if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); if (id == mDNSInterface_Any ) return(kDNSServiceInterfaceIndexAny); intf = (PosixNetworkInterface*)(m->HostInterfaces); @@ -1306,6 +1194,36 @@ mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) return(err == 0); } +#ifdef __NetBSD__ +#include <sys/param.h> +#include <sys/sysctl.h> + +void +initmachinedescr(mDNS *const m) +{ + char hwbuf[256], swbuf[256]; + size_t hwlen, swlen; + const int hwmib[] = { CTL_HW, HW_MODEL }; + const int swmib[] = { CTL_KERN, KERN_OSRELEASE }; + const char netbsd[] = "NetBSD "; + + hwlen = sizeof(hwbuf); + swlen = sizeof(swbuf); + if (sysctl(hwmib, 2, hwbuf, &hwlen, 0, 0) || + sysctl(swmib, 2, swbuf, &swlen, 0, 0)) + return; + + if (hwlen + swlen + sizeof(netbsd) >=254) + return; + + m->HIHardware.c[0] = hwlen - 1; + m->HISoftware.c[0] = swlen + sizeof(netbsd) - 2; + memcpy(&m->HIHardware.c[1], hwbuf, hwlen - 1); + memcpy(&m->HISoftware.c[1], netbsd, sizeof(netbsd) - 1); + memcpy(&m->HISoftware.c[1 + sizeof(netbsd) - 1], swbuf, swlen - 1); +} +#endif + // mDNS core calls this routine to initialise the platform-specific data. mDNSexport mStatus mDNSPlatformInit(mDNS *const m) { @@ -1327,6 +1245,10 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer"); +#ifdef __NetBSD__ + initmachinedescr(m); +#endif + mDNS_SetFQDN(m); sa.sa_family = AF_INET; diff --git a/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSUNP.c b/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSUNP.c index 5a4f95668af..1059c83cf15 100755 --- a/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSUNP.c +++ b/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSUNP.c @@ -13,141 +13,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - - Change History (most recent first): - -Log: mDNSUNP.c,v $ -Revision 1.40 2009/01/13 05:31:34 mkrochma -<rdar://problem/6491367> Replace bzero, bcopy with mDNSPlatformMemZero, mDNSPlatformMemCopy, memset, memcpy - -Revision 1.39 2009/01/11 03:20:06 mkrochma -<rdar://problem/5797526> Fixes from Igor Seleznev to get mdnsd working on Solaris - -Revision 1.38 2009/01/10 22:54:42 mkrochma -<rdar://problem/5797544> Fixes from Igor Seleznev to get mdnsd working on Linux - -Revision 1.37 2008/10/23 22:33:24 cheshire -Changed "NOTE:" to "Note:" so that BBEdit 9 stops putting those comment lines into the funtion popup menu - -Revision 1.36 2008/04/21 18:21:22 mkrochma -<rdar://problem/5877307> Need to free ifi_netmask -Submitted by Igor Seleznev - -Revision 1.35 2007/11/15 21:36:19 cheshire -<rdar://problem/5289340> POSIX: Off by one overflow in get_ifi_info_linuxv6() - -Revision 1.34 2006/08/14 23:24:47 cheshire -Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 - -Revision 1.33 2006/03/13 23:14:21 cheshire -<rdar://problem/4427969> Compile problems on FreeBSD -Use <netinet/in_var.h> instead of <netinet6/in6_var.h> - -Revision 1.32 2005/12/21 02:56:43 cheshire -<rdar://problem/4243433> get_ifi_info() should fake ifi_index when SIOCGIFINDEX undefined - -Revision 1.31 2005/12/21 02:46:05 cheshire -<rdar://problem/4243514> mDNSUNP.c needs to include <sys/param.h> on 4.4BSD Lite - -Revision 1.30 2005/11/29 20:03:02 mkrochma -Wrapped sin_len with #ifndef NOT_HAVE_SA_LEN - -Revision 1.29 2005/11/12 02:23:10 cheshire -<rdar://problem/4317680> mDNSUNP.c needs to deal with lame results from SIOCGIFNETMASK, SIOCGIFBRDADDR and SIOCGIFDSTADDR - -Revision 1.28 2005/10/31 22:09:45 cheshire -Buffer "char addr6[33]" was seven bytes too small - -Revision 1.27 2005/06/29 15:54:21 cheshire -<rdar://problem/4113742> mDNSResponder-107.1 does not work on FreeBSD -Refine last checkin so that it (hopefully) doesn't break get_ifi_info() for every other OS - -Revision 1.26 2005/04/08 21:43:59 ksekar -<rdar://problem/4083426> mDNSPosix (v98) retrieve interface list bug on AMD64 architecture -Submitted by Andrew de Quincey - -Revision 1.25 2005/04/08 21:37:57 ksekar -<rdar://problem/3792767> get_ifi_info doesn't return IPv6 interfaces on Linux - -Revision 1.24 2005/04/08 21:30:16 ksekar -<rdar://problem/4007457> Compiling problems with mDNSResponder-98 on Solaris/Sparc v9 -Patch submitted by Bernd Kuhls - -Revision 1.23 2004/12/01 04:25:05 cheshire -<rdar://problem/3872803> Darwin patches for Solaris and Suse -Provide daemon() for platforms that don't have it - -Revision 1.22 2004/11/30 22:37:01 cheshire -Update copyright dates and add "Mode: C; tab-width: 4" headers - -Revision 1.21 2004/11/08 22:13:59 rpantos -Create sockf6 lazily when v6 interface found. - -Revision 1.20 2004/10/16 00:17:01 cheshire -<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check - -Revision 1.19 2004/07/20 01:47:36 rpantos -NOT_HAVE_SA_LEN applies to v6, too. And use more-portable s6_addr. - -Revision 1.18 2004/07/08 21:30:21 rpantos - -Revision 1.17 2004/06/25 00:26:27 rpantos -Changes to fix the Posix build on Solaris. - -Revision 1.16 2004/03/20 05:37:09 cheshire -Fix contributed by Terry Lambert & Alfred Perlstein: -Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x - -Revision 1.15 2004/02/14 01:09:45 rpantos -Just use HAVE_IPV6 rather than defined(HAVE_IPV6). - -Revision 1.14 2003/12/11 18:53:40 cheshire -Fix compiler warning reported by Paul Guyot - -Revision 1.13 2003/12/08 20:47:02 rpantos -Add support for mDNSResponder on Linux. - -Revision 1.12 2003/09/02 20:47:13 cheshire -Fix signed/unsigned warning - -Revision 1.11 2003/08/12 19:56:26 cheshire -Update to APSL 2.0 - -Revision 1.10 2003/08/06 18:20:51 cheshire -Makefile cleanup - -Revision 1.9 2003/07/14 18:11:54 cheshire -Fix stricter compiler warnings - -Revision 1.8 2003/07/02 21:19:59 cheshire -<rdar://problem/3313413> Update copyright notices, etc., in source code comments - -Revision 1.7 2003/03/20 21:10:31 cheshire -Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris - -Revision 1.6 2003/03/13 03:46:21 cheshire -Fixes to make the code build on Linux - -Revision 1.5 2003/02/07 03:02:02 cheshire -Submitted by: Mitsutaka Watanabe -The code saying "index += 1;" was effectively making up random interface index values. -The right way to find the correct interface index is if_nametoindex(); - -Revision 1.4 2002/12/23 22:13:31 jgraessl - -Reviewed by: Stuart Cheshire -Initial IPv6 support for mDNSResponder. - -Revision 1.3 2002/09/21 20:44:53 zarzycki -Added APSL info - -Revision 1.2 2002/09/19 04:20:44 cheshire -Remove high-ascii characters that confuse some systems - -Revision 1.1 2002/09/17 06:24:34 cheshire -First checkin - -*/ + */ #include "mDNSUNP.h" @@ -207,8 +73,8 @@ void plen_to_mask(int plen, char *addr) { int bits_in_block=16; /* Bits per IPv6 block */ for(i=0;i<=colons;i++) { int block, ones=0xffff, ones_in_block; - if(plen>bits_in_block) ones_in_block=bits_in_block; - else ones_in_block=plen; + if (plen>bits_in_block) ones_in_block=bits_in_block; + else ones_in_block=plen; block = ones & (ones << (bits_in_block-ones_in_block)); i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block); plen -= ones_in_block; @@ -241,6 +107,8 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) addr[4],addr[5],addr[6],addr[7], &index, &plen, &scope, &flags, ifname) != EOF) { + char ipv6addr[INET6_ADDRSTRLEN]; + myflags = 0; if (strncmp(lastname, ifname, IFNAMSIZ) == 0) { if (doaliases == 0) @@ -275,7 +143,6 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6)); /* Add netmask of the interface */ - char ipv6addr[INET6_ADDRSTRLEN]; plen_to_mask(plen, ipv6addr); ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); if (ifi->ifi_addr == NULL) { @@ -338,7 +205,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) #endif #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX - if(family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); + if (family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); #endif sockfd = -1; diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/dns-sd.1 b/external/apache2/mDNSResponder/dist/mDNSShared/dns-sd.1 index 93ca9ae043b..1c48802ce50 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dns-sd.1 +++ b/external/apache2/mDNSResponder/dist/mDNSShared/dns-sd.1 @@ -14,27 +14,6 @@ .\" See the License for the specific language governing permissions and .\" limitations under the License. .\" -.\" Log: dns-sd.1,v $ -.\" Revision 1.6 2006/08/14 23:24:56 cheshire -.\" Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 -.\" -.\" Revision 1.5 2005/07/04 23:12:35 cheshire -.\" <rdar://problem/4103628> The dns-sd command first appeared in Mac OS X 10.4 (Tiger) -.\" -.\" Revision 1.4 2005/02/16 02:29:32 cheshire -.\" Update terminology -.\" -.\" Revision 1.3 2005/02/10 22:35:28 cheshire -.\" <rdar://problem/3727944> Update name -.\" -.\" Revision 1.2 2004/09/24 18:33:05 cheshire -.\" <rdar://problem/3561780> Update man pages to clarify that mDNS and dns-sd are not intended for script use -.\" -.\" Revision 1.1 2004/09/22 22:46:25 cheshire -.\" Man page for dns-sd command-line tool -.\" -.\" -.\" .Dd April 2004 \" Date .Dt dns-sd 1 \" Document Title .Os NetBSD \" Operating System diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/dns_sd.h b/external/apache2/mDNSResponder/dist/mDNSShared/dns_sd.h index d93f9724bf7..fe61f8cf5c0 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dns_sd.h +++ b/external/apache2/mDNSResponder/dist/mDNSShared/dns_sd.h @@ -77,12 +77,19 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 2120100 +#define _DNS_SD_H 2581400 #ifdef __cplusplus extern "C" { #endif +/* Set to 1 if libdispatch is supported + * Note: May also be set by project and/or Makefile + */ +#ifndef _DNS_SD_LIBDISPATCH +#define _DNS_SD_LIBDISPATCH 0 +#endif /* ndef _DNS_SD_LIBDISPATCH */ + /* standard calling convention under Win32 is __stdcall */ /* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ /* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ @@ -129,6 +136,10 @@ typedef INT32 int32_t; #include <stdint.h> #endif +#if _DNS_SD_LIBDISPATCH +#include <dispatch/dispatch.h> +#endif + /* DNSServiceRef, DNSRecordRef * * Opaque internal data types. @@ -331,8 +342,15 @@ enum */ kDNSServiceFlagsSuppressUnusable = 0x8000 - /* Placeholder definition, for future use - */ + /* + * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the + * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name) + * but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses + * for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly, + * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for + * "hostname". + */ + }; /* Possible protocols for DNSServiceNATPortMappingCreate(). */ @@ -368,73 +386,73 @@ enum enum { - kDNSServiceType_A = 1, /* Host address. */ - kDNSServiceType_NS = 2, /* Authoritative server. */ - kDNSServiceType_MD = 3, /* Mail destination. */ - kDNSServiceType_MF = 4, /* Mail forwarder. */ - kDNSServiceType_CNAME = 5, /* Canonical name. */ - kDNSServiceType_SOA = 6, /* Start of authority zone. */ - kDNSServiceType_MB = 7, /* Mailbox domain name. */ - kDNSServiceType_MG = 8, /* Mail group member. */ - kDNSServiceType_MR = 9, /* Mail rename name. */ - kDNSServiceType_NULL = 10, /* Null resource record. */ - kDNSServiceType_WKS = 11, /* Well known service. */ - kDNSServiceType_PTR = 12, /* Domain name pointer. */ - kDNSServiceType_HINFO = 13, /* Host information. */ - kDNSServiceType_MINFO = 14, /* Mailbox information. */ - kDNSServiceType_MX = 15, /* Mail routing information. */ - kDNSServiceType_TXT = 16, /* One or more text strings (NOT "zero or more..."). */ - kDNSServiceType_RP = 17, /* Responsible person. */ - kDNSServiceType_AFSDB = 18, /* AFS cell database. */ - kDNSServiceType_X25 = 19, /* X_25 calling address. */ - kDNSServiceType_ISDN = 20, /* ISDN calling address. */ - kDNSServiceType_RT = 21, /* Router. */ - kDNSServiceType_NSAP = 22, /* NSAP address. */ - kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ - kDNSServiceType_SIG = 24, /* Security signature. */ - kDNSServiceType_KEY = 25, /* Security key. */ - kDNSServiceType_PX = 26, /* X.400 mail mapping. */ - kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ - kDNSServiceType_AAAA = 28, /* IPv6 Address. */ - kDNSServiceType_LOC = 29, /* Location Information. */ - kDNSServiceType_NXT = 30, /* Next domain (security). */ - kDNSServiceType_EID = 31, /* Endpoint identifier. */ - kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ - kDNSServiceType_SRV = 33, /* Server Selection. */ - kDNSServiceType_ATMA = 34, /* ATM Address */ - kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ - kDNSServiceType_KX = 36, /* Key Exchange */ - kDNSServiceType_CERT = 37, /* Certification record */ - kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */ - kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ - kDNSServiceType_SINK = 40, /* Kitchen sink (experimental) */ - kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ - kDNSServiceType_APL = 42, /* Address Prefix List */ - kDNSServiceType_DS = 43, /* Delegation Signer */ - kDNSServiceType_SSHFP = 44, /* SSH Key Fingerprint */ - kDNSServiceType_IPSECKEY = 45, /* IPSECKEY */ - kDNSServiceType_RRSIG = 46, /* RRSIG */ - kDNSServiceType_NSEC = 47, /* Denial of Existence */ - kDNSServiceType_DNSKEY = 48, /* DNSKEY */ - kDNSServiceType_DHCID = 49, /* DHCP Client Identifier */ - kDNSServiceType_NSEC3 = 50, /* Hashed Authenticated Denial of Existence */ - kDNSServiceType_NSEC3PARAM= 51, /* Hashed Authenticated Denial of Existence */ - - kDNSServiceType_HIP = 55, /* Host Identity Protocol */ - - kDNSServiceType_SPF = 99, /* Sender Policy Framework for E-Mail */ - kDNSServiceType_UINFO = 100, /* IANA-Reserved */ - kDNSServiceType_UID = 101, /* IANA-Reserved */ - kDNSServiceType_GID = 102, /* IANA-Reserved */ - kDNSServiceType_UNSPEC = 103, /* IANA-Reserved */ - - kDNSServiceType_TKEY = 249, /* Transaction key */ - kDNSServiceType_TSIG = 250, /* Transaction signature. */ - kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ - kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ - kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ - kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ - kDNSServiceType_ANY = 255 /* Wildcard match. */ + kDNSServiceType_A = 1, /* Host address. */ + kDNSServiceType_NS = 2, /* Authoritative server. */ + kDNSServiceType_MD = 3, /* Mail destination. */ + kDNSServiceType_MF = 4, /* Mail forwarder. */ + kDNSServiceType_CNAME = 5, /* Canonical name. */ + kDNSServiceType_SOA = 6, /* Start of authority zone. */ + kDNSServiceType_MB = 7, /* Mailbox domain name. */ + kDNSServiceType_MG = 8, /* Mail group member. */ + kDNSServiceType_MR = 9, /* Mail rename name. */ + kDNSServiceType_NULL = 10, /* Null resource record. */ + kDNSServiceType_WKS = 11, /* Well known service. */ + kDNSServiceType_PTR = 12, /* Domain name pointer. */ + kDNSServiceType_HINFO = 13, /* Host information. */ + kDNSServiceType_MINFO = 14, /* Mailbox information. */ + kDNSServiceType_MX = 15, /* Mail routing information. */ + kDNSServiceType_TXT = 16, /* One or more text strings (NOT "zero or more..."). */ + kDNSServiceType_RP = 17, /* Responsible person. */ + kDNSServiceType_AFSDB = 18, /* AFS cell database. */ + kDNSServiceType_X25 = 19, /* X_25 calling address. */ + kDNSServiceType_ISDN = 20, /* ISDN calling address. */ + kDNSServiceType_RT = 21, /* Router. */ + kDNSServiceType_NSAP = 22, /* NSAP address. */ + kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ + kDNSServiceType_SIG = 24, /* Security signature. */ + kDNSServiceType_KEY = 25, /* Security key. */ + kDNSServiceType_PX = 26, /* X.400 mail mapping. */ + kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ + kDNSServiceType_AAAA = 28, /* IPv6 Address. */ + kDNSServiceType_LOC = 29, /* Location Information. */ + kDNSServiceType_NXT = 30, /* Next domain (security). */ + kDNSServiceType_EID = 31, /* Endpoint identifier. */ + kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ + kDNSServiceType_SRV = 33, /* Server Selection. */ + kDNSServiceType_ATMA = 34, /* ATM Address */ + kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ + kDNSServiceType_KX = 36, /* Key Exchange */ + kDNSServiceType_CERT = 37, /* Certification record */ + kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */ + kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ + kDNSServiceType_SINK = 40, /* Kitchen sink (experimental) */ + kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ + kDNSServiceType_APL = 42, /* Address Prefix List */ + kDNSServiceType_DS = 43, /* Delegation Signer */ + kDNSServiceType_SSHFP = 44, /* SSH Key Fingerprint */ + kDNSServiceType_IPSECKEY = 45, /* IPSECKEY */ + kDNSServiceType_RRSIG = 46, /* RRSIG */ + kDNSServiceType_NSEC = 47, /* Denial of Existence */ + kDNSServiceType_DNSKEY = 48, /* DNSKEY */ + kDNSServiceType_DHCID = 49, /* DHCP Client Identifier */ + kDNSServiceType_NSEC3 = 50, /* Hashed Authenticated Denial of Existence */ + kDNSServiceType_NSEC3PARAM = 51, /* Hashed Authenticated Denial of Existence */ + + kDNSServiceType_HIP = 55, /* Host Identity Protocol */ + + kDNSServiceType_SPF = 99, /* Sender Policy Framework for E-Mail */ + kDNSServiceType_UINFO = 100, /* IANA-Reserved */ + kDNSServiceType_UID = 101, /* IANA-Reserved */ + kDNSServiceType_GID = 102, /* IANA-Reserved */ + kDNSServiceType_UNSPEC = 103, /* IANA-Reserved */ + + kDNSServiceType_TKEY = 249, /* Transaction key */ + kDNSServiceType_TSIG = 250, /* Transaction signature. */ + kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ + kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ + kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ + kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ + kDNSServiceType_ANY = 255 /* Wildcard match. */ }; /* possible error code values */ @@ -514,7 +532,7 @@ enum * * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String * terminating NULL at the end). The regtype is of the form _service._tcp or - * _service._udp, where the "service" part is 1-14 characters, which may be + * _service._udp, where the "service" part is 1-15 characters, which may be * letters, digits, or hyphens. The domain part of the three-part name may be * any legal domain, providing that the resulting servicename+regtype+domain * name does not exceed 256 bytes. @@ -567,11 +585,30 @@ enum * accomplish this by inspecting the interfaceIndex of each service reported * to their DNSServiceBrowseReply() callback function, and discarding those * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. + * + * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, + * and Resolve operations. It should not be used in other DNSService APIs. + * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or + * DNSServiceQueryRecord, it restricts the operation to P2P. + * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is + * mapped internally to kDNSServiceInterfaceIndexAny, because resolving + * a P2P service may create and/or enable an interface whose index is not + * known a priori. The resolve callback will indicate the index of the + * interface via which the service can be accessed. + * + * If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse + * or DNSServiceQueryRecord, the operation will also include P2P. In this + * case, if a service instance or the record being queried is found over P2P, + * the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P as the + * interface index. */ #define kDNSServiceInterfaceIndexAny 0 #define kDNSServiceInterfaceIndexLocalOnly ((uint32_t)-1) #define kDNSServiceInterfaceIndexUnicast ((uint32_t)-2) +#define kDNSServiceInterfaceIndexP2P ((uint32_t)-3) typedef uint32_t DNSServiceFlags; typedef uint32_t DNSServiceProtocol; @@ -886,7 +923,7 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * * regtype: The service type followed by the protocol, separated by a dot * (e.g. "_ftp._tcp"). The service type must be an underscore, followed - * by 1-14 characters, which may be letters, digits, or hyphens. + * by 1-15 characters, which may be letters, digits, or hyphens. * The transport protocol must be "_tcp" or "_udp". New service types * should be registered at <http://www.dns-sd.org/ServiceTypes.html>. * @@ -912,6 +949,13 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * % dns-sd -B _test._tcp,HasFeatureA # finds "Better" and "Best" * % dns-sd -B _test._tcp,HasFeatureB # finds only "Best" * + * Subtype labels may be up to 63 bytes long, and may contain any eight- + * bit byte values, including zero bytes. However, due to the nature of + * using a C-string-based API, conventional DNS escaping must be used for + * dots ('.'), commas (','), backslashes ('\') and zero bytes, as shown below: + * + * % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123 + * * domain: If non-NULL, specifies the domain on which to advertise the service. * Most applications will not specify a domain, instead automatically * registering in the default domain(s). @@ -965,7 +1009,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister const char *regtype, const char *domain, /* may be NULL */ const char *host, /* may be NULL */ - uint16_t port, + uint16_t port, /* In network byte order */ uint16_t txtLen, const void *txtRecord, /* may be NULL */ DNSServiceRegisterReply callBack, /* may be NULL */ @@ -1267,7 +1311,7 @@ typedef void (DNSSD_API *DNSServiceResolveReply) DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, - uint16_t port, + uint16_t port, /* In network byte order */ uint16_t txtLen, const unsigned char *txtRecord, void *context @@ -1528,11 +1572,6 @@ typedef void (DNSSD_API *DNSServiceGetAddrInfoReply) * unlikely to be of any use anyway. Similarly, if this host has no routable * IPv4 address, the call will not try to look up IPv4 addresses for "hostname". * - * * If "hostname" is a link-local multicast DNS hostname (i.e. a ".local." name) - * but this host has no IPv6 address of any kind, then it will not try to look - * up IPv6 addresses for "hostname". Similarly, if this host has no IPv4 address - * of any kind, the call will not try to look up IPv4 addresses for "hostname". - * * hostname: The fully qualified domain name of the host to be queried for. * * callBack: The function to be called when the query succeeds or fails asynchronously. @@ -1735,7 +1774,8 @@ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord /* DNSServiceNATPortMappingCreate * * Request a port mapping in the NAT gateway, which maps a port on the local machine - * to an external port on the NAT. + * to an external port on the NAT. The NAT should support either the NAT-PMP or the UPnP IGD + * protocol for this API to create a successful mapping. * * The port mapping will be renewed indefinitely until the client process exits, or * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate(). @@ -1836,9 +1876,9 @@ typedef void (DNSSD_API *DNSServiceNATPortMappingReply) DNSServiceErrorType errorCode, uint32_t externalAddress, /* four byte IPv4 address in network byte order */ DNSServiceProtocol protocol, - uint16_t internalPort, - uint16_t externalPort, /* may be different than the requested port */ - uint32_t ttl, /* may be different than the requested ttl */ + uint16_t internalPort, /* In network byte order */ + uint16_t externalPort, /* In network byte order and may be different than the requested port */ + uint32_t ttl, /* may be different than the requested ttl */ void *context ); @@ -1940,10 +1980,10 @@ DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate DNSServiceErrorType DNSSD_API DNSServiceConstructFullName ( - char *fullName, - const char *service, /* may be NULL */ - const char *regtype, - const char *domain + char * const fullName, + const char * const service, /* may be NULL */ + const char * const regtype, + const char * const domain ); @@ -2299,41 +2339,56 @@ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex const void **value ); -#ifdef __APPLE_API_PRIVATE - +#if _DNS_SD_LIBDISPATCH /* - * Mac OS X specific functionality - * 3rd party clients of this API should not depend on future support or availability of this routine - */ +* DNSServiceSetDispatchQueue +* +* Allows you to schedule a DNSServiceRef on a serial dispatch queue for receiving asynchronous +* callbacks. It's the clients responsibility to ensure that the provided dispatch queue is running. +* +* A typical application that uses CFRunLoopRun or dispatch_main on its main thread will +* usually schedule DNSServiceRefs on its main queue (which is always a serial queue) +* using "DNSServiceSetDispatchQueue(sdref, dispatch_get_main_queue());" +* +* If there is any error during the processing of events, the application callback will +* be called with an error code. For shared connections, each subordinate DNSServiceRef +* will get its own error callback. Currently these error callbacks only happen +* if the mDNSResponder daemon is manually terminated or crashes, and the error +* code in this case is kDNSServiceErr_ServiceNotRunning. The application must call +* DNSServiceRefDeallocate to free the DNSServiceRef when it gets such an error code. +* These error callbacks are rare and should not normally happen on customer machines, +* but application code should be written defensively to handle such error callbacks +* gracefully if they occur. +* +* After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult +* on the same DNSServiceRef will result in undefined behavior and should be avoided. +* +* Once the application successfully schedules a DNSServiceRef on a serial dispatch queue using +* DNSServiceSetDispatchQueue, it cannot remove the DNSServiceRef from the dispatch queue, or use +* DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch +* queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until +* the application no longer requires that operation and terminates it using DNSServiceRefDeallocate. +* +* service: DNSServiceRef that was allocated and returned to the application, when the +* application calls one of the DNSService API. +* +* queue: dispatch queue where the application callback will be scheduled +* +* return value: Returns kDNSServiceErr_NoError on success. +* Returns kDNSServiceErr_NoMemory if it cannot create a dispatch source +* Returns kDNSServiceErr_BadParam if the service param is invalid or the +* queue param is invalid +*/ + +DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue + ( + DNSServiceRef service, + dispatch_queue_t queue + ); +#endif //_DNS_SD_LIBDISPATCH -/* DNSServiceSetDefaultDomainForUser() - * - * Set the default domain for the caller's UID. Future browse and registration - * calls by this user that do not specify an explicit domain will browse and - * register in this wide-area domain in addition to .local. In addition, this - * domain will be returned as a Browse domain via domain enumeration calls. - * - * Parameters: - * - * flags: Pass kDNSServiceFlagsAdd to add a domain for a user. Call without - * this flag set to clear a previously added domain. - * - * domain: The domain to be used for the caller's UID. - * - * return value: Returns kDNSServiceErr_NoError on success, otherwise returns - * an error code indicating the error that occurred. - */ - -DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser - ( - DNSServiceFlags flags, - const char *domain - ); +#ifdef __APPLE_API_PRIVATE -/* Symbol defined to tell System Configuration Framework where to look in the Dynamic Store - * for the list of PrivateDNS domains that need to be handed off to mDNSResponder - * (the complete key is "State:/Network/PrivateDNS") - */ #define kDNSServiceCompPrivateDNS "PrivateDNS" #define kDNSServiceCompMulticastDNS "MulticastDNS" diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientlib.c b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientlib.c index 4f30820c610..8abbb6d0896 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientlib.c +++ b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientlib.c @@ -24,79 +24,6 @@ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Change History (most recent first): - -Log: dnssd_clientlib.c,v $ -Revision 1.21 2009/04/01 21:10:11 herscher -<rdar://problem/5925472> Current Bonjour code does not compile on Windows. Use _stricmp and _strnicmp. - -Revision 1.20 2008/11/26 20:57:37 cheshire -For consistency with other similar macros, renamed mdnsIsDigit/mdnsIsLetter/mdnsValidHostChar -to mDNSIsDigit/mDNSIsLetter/mDNSValidHostChar - -Revision 1.19 2008/11/04 21:15:18 cheshire -<rdar://problem/5969564> Potential buffer overflows in DNSServiceConstructFullName - -Revision 1.18 2007/11/30 23:06:10 cheshire -Fixed compile warning: declaration of 'index' shadows a global declaration - -Revision 1.17 2007/10/02 19:36:04 cheshire -<rdar://problem/5516444> TXTRecordGetValuePtr should be case-insenstive - -Revision 1.16 2007/09/18 19:09:02 cheshire -<rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings - -Revision 1.15 2007/07/28 00:00:43 cheshire -Renamed CompileTimeAssertionCheck structure for consistency with others - -Revision 1.14 2007/03/20 17:07:16 cheshire -Rename "struct uDNS_TCPSocket_struct" to "TCPSocket", "struct uDNS_UDPSocket_struct" to "UDPSocket" - -Revision 1.13 2007/02/27 00:25:03 cheshire -<rdar://problem/5010640> DNSServiceConstructFullName() doesn't handle empty string for instance name - -Revision 1.12 2007/02/07 19:32:00 cheshire -<rdar://problem/4980353> All mDNSResponder components should contain version strings in SCCS-compatible format - -Revision 1.11 2006/08/14 23:05:53 cheshire -Added "tab-width" emacs header line - -Revision 1.10 2005/04/06 02:06:56 shersche -Add DNSSD_API macro to TXTRecord API calls - -Revision 1.9 2004/10/06 02:22:19 cheshire -Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" - -Revision 1.8 2004/10/01 22:15:55 rpantos -rdar://problem/3824265: Replace APSL in client lib with BSD license. - -Revision 1.7 2004/06/26 03:16:34 shersche -clean up warning messages on Win32 platform - -Submitted by: herscher - -Revision 1.6 2004/06/12 01:09:45 cheshire -To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.) -API routines have to be declared as "__stdcall", instead of the C default, "__cdecl" - -Revision 1.5 2004/05/25 18:29:33 cheshire -Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, -so that it's also accessible to dnssd_clientshim.c (single address space) clients. - -Revision 1.4 2004/05/25 17:08:55 cheshire -Fix compiler warning (doesn't make sense for function return type to be const) - -Revision 1.3 2004/05/21 21:41:35 cheshire -Add TXT record building and parsing APIs - -Revision 1.2 2004/05/20 22:22:21 cheshire -Enable code that was bracketed by "#if 0" - -Revision 1.1 2004/03/12 21:30:29 cheshire -Build a System-Context Shared Library from mDNSCore, for the benefit of developers -like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. - */ #include <stdlib.h> diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientstub.c b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientstub.c index 47971230baf..14e3e86bfde 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientstub.c +++ b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientstub.c @@ -24,290 +24,7 @@ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Change History (most recent first): - -Log: dnssd_clientstub.c,v $ -Revision 1.134 2009/06/19 23:13:24 cheshire -<rdar://problem/6990066> Library: crash at handle_resolve_response + 183 -Added check for NULL after calling get_string - -Revision 1.133 2009/05/27 22:19:12 cheshire -Remove questionable uses of errno - -Revision 1.132 2009/05/26 21:31:07 herscher -Fix compile errors on Windows - -Revision 1.131 2009/05/26 04:48:19 herscher -<rdar://problem/6844819> ExplorerPlugin does not work in B4W 2.0 - -Revision 1.130 2009/05/02 01:29:48 mcguire -<rdar://problem/6847601> spin calling DNSServiceProcessResult if errno was set to EWOULDBLOCK by an unrelated call - -Revision 1.129 2009/05/01 19:18:50 cheshire -<rdar://problem/6843645> Using duplicate DNSServiceRefs when sharing a connection should return an error - -Revision 1.128 2009/04/01 21:09:35 herscher -<rdar://problem/5925472> Current Bonjour code does not compile on Windows. - -Revision 1.127 2009/03/03 21:38:19 cheshire -Improved "deliver_request ERROR" message - -Revision 1.126 2009/02/12 21:02:22 cheshire -Commented out BPF "Sending fd" debugging message - -Revision 1.125 2009/02/12 20:28:32 cheshire -Added some missing "const" declarations - -Revision 1.124 2009/02/10 01:44:39 cheshire -<rdar://problem/6553729> DNSServiceUpdateRecord fails with kDNSServiceErr_BadReference for otherwise valid reference - -Revision 1.123 2009/01/19 00:49:21 mkrochma -Type cast size_t values to unsigned long - -Revision 1.122 2009/01/18 03:51:37 mkrochma -Fix warning in deliver_request on Linux - -Revision 1.121 2009/01/16 23:34:37 cheshire -<rdar://problem/6504143> Uninitialized error code variable in error handling path in deliver_request - -Revision 1.120 2009/01/13 05:31:35 mkrochma -<rdar://problem/6491367> Replace bzero, bcopy with mDNSPlatformMemZero, mDNSPlatformMemCopy, memset, memcpy - -Revision 1.119 2009/01/11 03:45:08 mkrochma -Stop type casting num_written and num_read to int - -Revision 1.118 2009/01/11 03:20:06 mkrochma -<rdar://problem/5797526> Fixes from Igor Seleznev to get mdnsd working on Solaris - -Revision 1.117 2009/01/10 22:03:43 mkrochma -<rdar://problem/5797507> dnsextd fails to build on Linux - -Revision 1.116 2009/01/05 16:55:24 cheshire -<rdar://problem/6452199> Stuck in "Examining available disks" -ConnectionResponse handler was accidentally matching the parent DNSServiceRef before -finding the appropriate subordinate DNSServiceRef for the operation in question. - -Revision 1.115 2008/12/18 00:19:11 mcguire -<rdar://problem/6452199> Stuck in "Examining available disks" - -Revision 1.114 2008/12/10 02:11:43 cheshire -ARMv5 compiler doesn't like uncommented stuff after #endif - -Revision 1.113 2008/12/04 03:23:05 cheshire -Preincrement UID counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear - -Revision 1.112 2008/11/25 22:56:54 cheshire -<rdar://problem/6377257> Make library code more defensive when client calls DNSServiceProcessResult with bad DNSServiceRef repeatedly - -Revision 1.111 2008/10/28 17:58:44 cheshire -If client code keeps calling DNSServiceProcessResult repeatedly after an error, rate-limit the -"DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function" log messages - -Revision 1.110 2008/10/23 23:38:58 cheshire -For Windows compatibility, instead of "strerror(errno)" use "dnssd_strerror(dnssd_errno)" - -Revision 1.109 2008/10/23 23:06:17 cheshire -Removed () from dnssd_errno macro definition -- it's not a function and doesn't need any arguments - -Revision 1.108 2008/10/23 22:33:24 cheshire -Changed "NOTE:" to "Note:" so that BBEdit 9 stops putting those comment lines into the funtion popup menu - -Revision 1.107 2008/10/20 21:50:11 cheshire -Improved /dev/bpf error message - -Revision 1.106 2008/10/20 15:37:18 cheshire -Log error message if opening /dev/bpf fails - -Revision 1.105 2008/09/27 01:26:34 cheshire -Added handler to pass back BPF fd when requested - -Revision 1.104 2008/09/23 01:36:00 cheshire -Updated code to use internalPort/externalPort terminology, instead of the old privatePort/publicPort -terms (which could be misleading, because the word "private" suggests security). - -Revision 1.103 2008/07/24 18:51:13 cheshire -Removed spurious spaces - -Revision 1.102 2008/02/25 19:16:19 cheshire -<rdar://problem/5708953> Problems with DNSServiceGetAddrInfo API -Was returning a bogus result (NULL pointer) when following a CNAME referral - -Revision 1.101 2008/02/20 21:18:21 cheshire -<rdar://problem/5708953> DNSServiceGetAddrInfo doesn't set the scope ID of returned IPv6 link local addresses - -Revision 1.100 2007/11/02 17:56:37 cheshire -<rdar://problem/5565787> Bonjour API broken for 64-bit apps (SCM_RIGHTS sendmsg fails) -Wrap hack code in "#if APPLE_OSX_mDNSResponder" since (as far as we know right now) -we don't want to do this on 64-bit Linux, Solaris, etc. - -Revision 1.99 2007/11/02 17:29:40 cheshire -<rdar://problem/5565787> Bonjour API broken for 64-bit apps (SCM_RIGHTS sendmsg fails) -To get 64-bit code that works, we need to NOT use the standard CMSG_* macros - -Revision 1.98 2007/11/01 19:52:43 cheshire -Wrap debugging messages in "#if DEBUG_64BIT_SCM_RIGHTS" - -Revision 1.97 2007/11/01 19:45:55 cheshire -Added "DEBUG_64BIT_SCM_RIGHTS" debugging code -See <rdar://problem/5565787> Bonjour API broken for 64-bit apps (SCM_RIGHTS sendmsg fails) - -Revision 1.96 2007/11/01 15:59:33 cheshire -umask not being set and restored properly in USE_NAMED_ERROR_RETURN_SOCKET code -(no longer used on OS X, but relevant for other platforms) - -Revision 1.95 2007/10/31 20:07:16 cheshire -<rdar://problem/5541498> Set SO_NOSIGPIPE on client socket -Refinement: the cleanup code still needs to close listenfd when necesssary - -Revision 1.94 2007/10/15 22:34:27 cheshire -<rdar://problem/5541498> Set SO_NOSIGPIPE on client socket - -Revision 1.93 2007/10/10 00:48:54 cheshire -<rdar://problem/5526379> Daemon spins in an infinite loop when it doesn't get the control message it's expecting - -Revision 1.92 2007/10/06 03:44:44 cheshire -Testing code for <rdar://problem/5526374> kqueue does not get a kevent to wake it up when a control message arrives on a socket - -Revision 1.91 2007/10/04 20:53:59 cheshire -Improved debugging message when sendmsg fails - -Revision 1.90 2007/09/30 00:09:27 cheshire -<rdar://problem/5492315> Pass socket fd via SCM_RIGHTS sendmsg instead of using named UDS in the filesystem - -Revision 1.89 2007/09/19 23:53:12 cheshire -Fixed spelling mistake in comment - -Revision 1.88 2007/09/07 23:18:27 cheshire -<rdar://problem/5467542> Change "client_context" to be an incrementing 64-bit counter - -Revision 1.87 2007/09/07 22:50:09 cheshire -Added comment explaining moreptr field in DNSServiceOp structure - -Revision 1.86 2007/09/07 20:21:22 cheshire -<rdar://problem/5462371> Make DNSSD library more resilient -Add more comments explaining the moreptr/morebytes logic; don't allow DNSServiceRefSockFD or -DNSServiceProcessResult for subordinate DNSServiceRefs created using kDNSServiceFlagsShareConnection - -Revision 1.85 2007/09/06 21:43:23 cheshire -<rdar://problem/5462371> Make DNSSD library more resilient -Allow DNSServiceRefDeallocate from within DNSServiceProcessResult callback - -Revision 1.84 2007/09/06 18:31:47 cheshire -<rdar://problem/5462371> Make DNSSD library more resilient against client programming errors - -Revision 1.83 2007/08/28 20:45:45 cheshire -Typo: ctrl_path needs to be 64 bytes, not 44 bytes - -Revision 1.82 2007/08/28 19:53:52 cheshire -<rdar://problem/5437423> Bonjour failures when /tmp is not writable (e.g. when booted from installer disc) - -Revision 1.81 2007/07/27 00:03:20 cheshire -Fixed compiler warnings that showed up now we're building optimized ("-Os") - -Revision 1.80 2007/07/23 22:12:53 cheshire -<rdar://problem/5352299> Make mDNSResponder more defensive against malicious local clients - -Revision 1.79 2007/07/23 19:58:24 cheshire -<rdar://problem/5351640> Library: Leak in DNSServiceRefDeallocate - -Revision 1.78 2007/07/12 20:42:27 cheshire -<rdar://problem/5280735> If daemon is killed, return kDNSServiceErr_ServiceNotRunning -to clients instead of kDNSServiceErr_Unknown - -Revision 1.77 2007/07/02 23:07:13 cheshire -<rdar://problem/5308280> Reduce DNS-SD client syslog error messages - -Revision 1.76 2007/06/22 20:12:18 cheshire -<rdar://problem/5277024> Leak in DNSServiceRefDeallocate - -Revision 1.75 2007/05/23 18:59:22 cheshire -Remove unnecessary IPC_FLAGS_REUSE_SOCKET - -Revision 1.74 2007/05/22 18:28:38 cheshire -Fixed compile errors in posix build - -Revision 1.73 2007/05/22 01:20:47 cheshire -To determine current operation, need to check hdr->op, not sdr->op - -Revision 1.72 2007/05/22 01:07:42 cheshire -<rdar://problem/3563675> API: Need a way to get version/feature information - -Revision 1.71 2007/05/18 23:55:22 cheshire -<rdar://problem/4454655> Allow multiple register/browse/resolve operations to share single Unix Domain Socket - -Revision 1.70 2007/05/17 20:58:22 cheshire -<rdar://problem/4647145> DNSServiceQueryRecord should return useful information with NXDOMAIN - -Revision 1.69 2007/05/16 16:58:27 cheshire -<rdar://problem/4471320> Improve reliability of kDNSServiceFlagsMoreComing flag on multiprocessor machines -As long as select indicates that data is waiting, loop within DNSServiceProcessResult delivering additional results - -Revision 1.68 2007/05/16 01:06:52 cheshire -<rdar://problem/4471320> Improve reliability of kDNSServiceFlagsMoreComing flag on multiprocessor machines - -Revision 1.67 2007/05/15 21:57:16 cheshire -<rdar://problem/4608220> Use dnssd_SocketValid(x) macro instead of just -assuming that all negative values (or zero!) are invalid socket numbers - -Revision 1.66 2007/03/27 22:23:04 cheshire -Add "dnssd_clientstub" prefix onto syslog messages - -Revision 1.65 2007/03/21 22:25:23 cheshire -<rdar://problem/4172796> Remove client retry logic now that mDNSResponder uses launchd for its Unix Domain Socket - -Revision 1.64 2007/03/21 19:01:56 cheshire -<rdar://problem/5078494> IPC code not 64-bit-savvy: assumes long=32bits, and short=16bits - -Revision 1.63 2007/03/12 21:48:21 cheshire -<rdar://problem/5000162> Scary unlink errors in system.log -Code was using memory after it had been freed - -Revision 1.62 2007/02/28 01:44:30 cheshire -<rdar://problem/5027863> Byte order bugs in uDNS.c, uds_daemon.c, dnssd_clientstub.c - -Revision 1.61 2007/02/09 03:09:42 cheshire -<rdar://problem/3869251> Cleanup: Stop returning kDNSServiceErr_Unknown so often -<rdar://problem/4177924> API: Should return kDNSServiceErr_ServiceNotRunning - -Revision 1.60 2007/02/08 20:33:44 cheshire -<rdar://problem/4985095> Leak on error path in DNSServiceProcessResult - -Revision 1.59 2007/01/05 08:30:55 cheshire -Trim excessive "Log" checkin history from before 2006 -(checkin history still available via "cvs log ..." of course) - -Revision 1.58 2006/10/27 00:38:22 cheshire -Strip accidental trailing whitespace from lines - -Revision 1.57 2006/09/30 01:06:54 cheshire -Protocol field should be uint32_t - -Revision 1.56 2006/09/27 00:44:16 herscher -<rdar://problem/4249761> API: Need DNSServiceGetAddrInfo() - -Revision 1.55 2006/09/26 01:52:01 herscher -<rdar://problem/4245016> NAT Port Mapping API (for both NAT-PMP and UPnP Gateway Protocol) - -Revision 1.54 2006/09/21 21:34:09 cheshire -<rdar://problem/4100000> Allow empty string name when using kDNSServiceFlagsNoAutoRename - -Revision 1.53 2006/09/07 04:43:12 herscher -Fix compile error on Win32 platform by moving inclusion of syslog.h - -Revision 1.52 2006/08/15 23:04:21 mkrochma -<rdar://problem/4090354> Client should be able to specify service name w/o callback - -Revision 1.51 2006/07/24 23:45:55 cheshire -<rdar://problem/4605276> DNSServiceReconfirmRecord() should return error code - -Revision 1.50 2006/06/28 08:22:27 cheshire -<rdar://problem/4605264> dnssd_clientstub.c needs to report unlink failures in syslog - -Revision 1.49 2006/06/28 07:58:59 cheshire -Minor textual tidying - -*/ + */ #include <errno.h> #include <stdlib.h> @@ -345,6 +62,7 @@ Minor textual tidying int len; char * buffer; DWORD err = WSAGetLastError(); + (void) priority; va_start( args, message ); len = _vscprintf( message, args ) + 1; buffer = malloc( len * sizeof(char) ); @@ -394,25 +112,34 @@ typedef void (*ProcessReplyFn)(DNSServiceOp *const sdr, const CallbackHeader *co // When using kDNSServiceFlagsShareConnection, there is one primary _DNSServiceOp_t, and zero or more subordinates // For the primary, the 'next' field points to the first subordinate, and its 'next' field points to the next, and so on. // For the primary, the 'primary' field is NULL; for subordinates the 'primary' field points back to the associated primary +// +// _DNS_SD_LIBDISPATCH is defined where libdispatch/GCD is available. This does not mean that the application will use the +// DNSServiceSetDispatchQueue API. Hence any new code guarded with _DNS_SD_LIBDISPATCH should still be backwards compatible. struct _DNSServiceRef_t { - DNSServiceOp *next; // For shared connection - DNSServiceOp *primary; // For shared connection - dnssd_sock_t sockfd; // Connected socket between client and daemon - dnssd_sock_t validator; // Used to detect memory corruption, double disposals, etc. - client_context_t uid; // For shared connection requests, each subordinate DNSServiceRef has its own ID, + DNSServiceOp *next; // For shared connection + DNSServiceOp *primary; // For shared connection + dnssd_sock_t sockfd; // Connected socket between client and daemon + dnssd_sock_t validator; // Used to detect memory corruption, double disposals, etc. + client_context_t uid; // For shared connection requests, each subordinate DNSServiceRef has its own ID, // unique within the scope of the same shared parent DNSServiceRef - uint32_t op; // request_op_t or reply_op_t - uint32_t max_index; // Largest assigned record index - 0 if no additional records registered - uint32_t logcounter; // Counter used to control number of syslog messages we write - int *moreptr; // Set while DNSServiceProcessResult working on this particular DNSServiceRef - ProcessReplyFn ProcessReply; // Function pointer to the code to handle received messages - void *AppCallback; // Client callback function and context - void *AppContext; + uint32_t op; // request_op_t or reply_op_t + uint32_t max_index; // Largest assigned record index - 0 if no additional records registered + uint32_t logcounter; // Counter used to control number of syslog messages we write + int *moreptr; // Set while DNSServiceProcessResult working on this particular DNSServiceRef + ProcessReplyFn ProcessReply; // Function pointer to the code to handle received messages + void *AppCallback; // Client callback function and context + void *AppContext; + DNSRecord *rec; +#if _DNS_SD_LIBDISPATCH + dispatch_source_t disp_source; + dispatch_queue_t disp_queue; +#endif }; struct _DNSRecordRef_t { + DNSRecord *recnext; void *AppContext; DNSServiceRegisterRecordReply AppCallback; DNSRecordRef recref; @@ -421,20 +148,35 @@ struct _DNSRecordRef_t }; // Write len bytes. Return 0 on success, -1 on error -static int write_all(dnssd_sock_t sd, char *buf, int len) +static int write_all(dnssd_sock_t sd, char *buf, size_t len) { // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. //if (send(sd, buf, len, MSG_WAITALL) != len) return -1; while (len) { - ssize_t num_written = send(sd, buf, len, 0); - if (num_written < 0 || num_written > len) + ssize_t num_written = send(sd, buf, (long)len, 0); + if (num_written < 0 || (size_t)num_written > len) { // Should never happen. If it does, it indicates some OS bug, // or that the mDNSResponder daemon crashed (which should never happen). - syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %zd/%d %d %s", sd, num_written, len, + #if !defined(__ppc__) && defined(SO_ISDEFUNCT) + int defunct; + socklen_t dlen = sizeof (defunct); + if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0) + syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + if (!defunct) + syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, + (long)num_written, (long)len, + (num_written < 0) ? dnssd_errno : 0, + (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); + else + syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd); + #else + syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, + (long)num_written, (long)len, (num_written < 0) ? dnssd_errno : 0, (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); + #endif return -1; } buf += num_written; @@ -445,7 +187,7 @@ static int write_all(dnssd_sock_t sd, char *buf, int len) enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2 }; -// Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for +// Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for static int read_all(dnssd_sock_t sd, char *buf, int len) { // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. @@ -456,11 +198,32 @@ static int read_all(dnssd_sock_t sd, char *buf, int len) ssize_t num_read = recv(sd, buf, len, 0); if ((num_read == 0) || (num_read < 0) || (num_read > len)) { + int printWarn = 0; + int defunct = 0; // Should never happen. If it does, it indicates some OS bug, // or that the mDNSResponder daemon crashed (which should never happen). - syslog(LOG_WARNING, "dnssd_clientstub read_all(%d) failed %zd/%d %d %s", sd, num_read, len, - (num_read < 0) ? dnssd_errno : 0, - (num_read < 0) ? dnssd_strerror(dnssd_errno) : ""); +#if defined(WIN32) + // <rdar://problem/7481776> Suppress logs for "A non-blocking socket operation + // could not be completed immediately" + if (WSAGetLastError() != WSAEWOULDBLOCK) + printWarn = 1; +#endif +#if !defined(__ppc__) && defined(SO_ISDEFUNCT) + { + socklen_t dlen = sizeof (defunct); + if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0) + syslog(LOG_WARNING, "dnssd_clientstub read_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + if (!defunct) + printWarn = 1; +#endif + if (printWarn) + syslog(LOG_WARNING, "dnssd_clientstub read_all(%d) failed %ld/%ld %d %s", sd, + (long)num_read, (long)len, + (num_read < 0) ? dnssd_errno : 0, + (num_read < 0) ? dnssd_strerror(dnssd_errno) : ""); + else if (defunct) + syslog(LOG_INFO, "dnssd_clientstub read_all(%d) DEFUNCT", sd); return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : read_all_fail; } buf += num_read; @@ -474,9 +237,29 @@ static int more_bytes(dnssd_sock_t sd) { struct timeval tv = { 0, 0 }; fd_set readfds; - FD_ZERO(&readfds); - FD_SET(sd, &readfds); - return(select(sd+1, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv) > 0); + fd_set *fs; + int ret; + + if (sd < FD_SETSIZE) + { + fs = &readfds; + FD_ZERO(fs); + } + else + { + // Compute the number of integers needed for storing "sd". Internally fd_set is stored + // as an array of ints with one bit for each fd and hence we need to compute + // the number of ints needed rather than the number of bytes. If "sd" is 32, we need + // two ints and not just one. + int nfdbits = sizeof (int) * 8; + int nints = (sd/nfdbits) + 1; + fs = (fd_set *)calloc(nints, sizeof(int)); + if (fs == NULL) { syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); return 0; } + } + FD_SET(sd, fs); + ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (fs != &readfds) free(fs); + return (ret > 0); } /* create_hdr @@ -503,11 +286,11 @@ static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int #if defined(USE_TCP_LOOPBACK) *len += 2; // Allocate space for two-byte port number #elif defined(USE_NAMED_ERROR_RETURN_SOCKET) - struct timeval time; - if (gettimeofday(&time, NULL) < 0) + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: gettimeofday failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); return NULL; } sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), - (unsigned long)(time.tv_sec & 0xFFF), (unsigned long)(time.tv_usec)); + (unsigned long)(tv.tv_sec & 0xFFF), (unsigned long)(tv.tv_usec)); *len += strlen(ctrl_path) + 1; #else *len += 1; // Allocate space for single zero byte (empty C string) @@ -540,9 +323,20 @@ static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int return hdr; } +static void FreeDNSRecords(DNSServiceOp *sdRef) + { + DNSRecord *rec = sdRef->rec; + while (rec) + { + DNSRecord *next = rec->recnext; + free(rec); + rec = next; + } + } + static void FreeDNSServiceOp(DNSServiceOp *x) { - // We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed + // We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed // then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket) if ((x->sockfd ^ x->validator) != ValidatorBits) syslog(LOG_WARNING, "dnssd_clientstub attempt to dispose invalid DNSServiceRef %p %08X %08X", x, x->sockfd, x->validator); @@ -559,6 +353,16 @@ static void FreeDNSServiceOp(DNSServiceOp *x) x->ProcessReply = NULL; x->AppCallback = NULL; x->AppContext = NULL; + x->rec = NULL; +#if _DNS_SD_LIBDISPATCH + if (x->disp_source) dispatch_release(x->disp_source); + x->disp_source = NULL; + x->disp_queue = NULL; +#endif + // DNSRecords may have been added to subordinate sdRef e.g., DNSServiceRegister/DNSServiceAddRecord + // or on the main sdRef e.g., DNSServiceCreateConnection/DNSServiveRegisterRecord. DNSRecords may have + // been freed if the application called DNSRemoveRecord + FreeDNSRecords(x); free(x); } } @@ -619,6 +423,11 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f sdr->ProcessReply = ProcessReply; sdr->AppCallback = AppCallback; sdr->AppContext = AppContext; + sdr->rec = NULL; +#if _DNS_SD_LIBDISPATCH + sdr->disp_source = NULL; + sdr->disp_queue = NULL; +#endif if (flags & kDNSServiceFlagsShareConnection) { @@ -659,6 +468,13 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f #else saddr.sun_family = AF_LOCAL; strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH); + #if !defined(__ppc__) && defined(SO_DEFUNCTOK) + { + int defunct = 1; + if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + #endif #endif while (1) @@ -758,6 +574,13 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) { errsd = sp[0]; // We'll read our four-byte error code from sp[0] listenfd = sp[1]; // We'll send sp[1] to the daemon + #if !defined(__ppc__) && defined(SO_DEFUNCTOK) + { + int defunct = 1; + if (setsockopt(errsd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0) + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); + } + #endif } } #endif @@ -932,6 +755,69 @@ int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) return (int) sdRef->sockfd; } +#if _DNS_SD_LIBDISPATCH +static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error) + { + DNSServiceOp *sdr = sdRef; + DNSServiceOp *sdrNext; + DNSRecord *rec; + DNSRecord *recnext; + int morebytes; + + while (sdr) + { + // We can't touch the sdr after the callback as it can be deallocated in the callback + sdrNext = sdr->next; + morebytes = 1; + sdr->moreptr = &morebytes; + switch (sdr->op) + { + case resolve_request: + if (sdr->AppCallback)((DNSServiceResolveReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, sdr->AppContext); + break; + case query_request: + if (sdr->AppCallback)((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, 0, sdr->AppContext); + break; + case addrinfo_request: + if (sdr->AppCallback)((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, NULL, 0, sdr->AppContext); + break; + case browse_request: + if (sdr->AppCallback)((DNSServiceBrowseReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, NULL, sdr->AppContext); + break; + case reg_service_request: + if (sdr->AppCallback)((DNSServiceRegisterReply) sdr->AppCallback)(sdr, 0, error, NULL, 0, NULL, sdr->AppContext); + break; + case enumeration_request: + if (sdr->AppCallback)((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, sdr->AppContext); + break; + case connection_request: + // This means Register Record, walk the list of DNSRecords to do the callback + rec = sdr->rec; + while (rec) + { + recnext = rec->recnext; + if (rec->AppCallback) ((DNSServiceRegisterRecordReply)rec->AppCallback)(sdr, 0, 0, error, rec->AppContext); + // The Callback can call DNSServiceRefDeallocate which in turn frees sdr and all the records. + // Detect that and return early + if (!morebytes){syslog(LOG_WARNING, "dnssdclientstub:Record: CallbackwithError morebytes zero"); return;} + rec = recnext; + } + break; + case port_mapping_request: + if (sdr->AppCallback)((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, 0, 0, error, 0, 0, 0, 0, 0, sdr->AppContext); + break; + default: + syslog(LOG_WARNING, "dnssd_clientstub CallbackWithError called with bad op %d", sdr->op); + } + // If DNSServiceRefDeallocate was called in the callback, morebytes will be zero. It means + // all other sdrefs have been freed. This happens for shared connections where the + // DNSServiceRefDeallocate on the first sdRef frees all other sdrefs. + if (!morebytes){syslog(LOG_WARNING, "dnssdclientstub:sdRef: CallbackwithError morebytes zero"); return;} + sdr = sdrNext; + } + } +#endif // _DNS_SD_LIBDISPATCH + // Handle reply from server, calling application client callback. If there is no reply // from the daemon on the socket contained in sdRef, the call will block. DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) @@ -968,11 +854,26 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) // return NoError on EWOULDBLOCK. This will handle the case // where a non-blocking socket is told there is data, but it was a false positive. // On error, read_all will write a message to syslog for us, so don't need to duplicate that here - // Note: If we want to properly support using non-blocking sockets in the future + // Note: If we want to properly support using non-blocking sockets in the future int result = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr)); if (result == read_all_fail) { + // Set the ProcessReply to NULL before callback as the sdRef can get deallocated + // in the callback. sdRef->ProcessReply = NULL; +#if _DNS_SD_LIBDISPATCH + // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult + // is not called by the application and hence need to communicate the error. Cancel the + // source so that we don't get any more events + if (sdRef->disp_source) + { + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning); + } +#endif + // Don't touch sdRef anymore as it might have been deallocated return kDNSServiceErr_ServiceNotRunning; } else if (result == read_all_wouldblock) @@ -997,8 +898,23 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) if (!data) return kDNSServiceErr_NoMemory; if (read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen) < 0) // On error, read_all will write a message to syslog for us { - free(data); + // Set the ProcessReply to NULL before callback as the sdRef can get deallocated + // in the callback. sdRef->ProcessReply = NULL; +#if _DNS_SD_LIBDISPATCH + // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult + // is not called by the application and hence need to communicate the error. Cancel the + // source so that we don't get any more events + if (sdRef->disp_source) + { + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning); + } +#endif + // Don't touch sdRef anymore as it might have been deallocated + free(data); return kDNSServiceErr_ServiceNotRunning; } else @@ -1056,16 +972,43 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef) char *ptr; size_t len = 0; ipc_msg_hdr *hdr = create_hdr(cancel_request, &len, &ptr, 0, sdRef); - ConvertHeaderBytes(hdr); - write_all(sdRef->sockfd, (char *)hdr, len); - free(hdr); + if (hdr) + { + ConvertHeaderBytes(hdr); + write_all(sdRef->sockfd, (char *)hdr, len); + free(hdr); + } *p = sdRef->next; FreeDNSServiceOp(sdRef); } } else // else, make sure to terminate all subordinates as well { +#if _DNS_SD_LIBDISPATCH + // The cancel handler will close the fd if a dispatch source has been set + if (sdRef->disp_source) + { + // By setting the ProcessReply to NULL, we make sure that we never call + // the application callbacks ever, after returning from this function. We + // assume that DNSServiceRefDeallocate is called from the serial queue + // that was passed to DNSServiceSetDispatchQueue. Hence, dispatch_source_cancel + // should cancel all the blocks on the queue and hence there should be no more + // callbacks when we return from this function. Setting ProcessReply to NULL + // provides extra protection. + sdRef->ProcessReply = NULL; + dispatch_source_cancel(sdRef->disp_source); + dispatch_release(sdRef->disp_source); + sdRef->disp_source = NULL; + } + // if disp_queue is set, it means it used the DNSServiceSetDispatchQueue API. In that case, + // when the source was cancelled, the fd was closed in the handler. Currently the source + // is cancelled only when the mDNSResponder daemon dies + else if (!sdRef->disp_queue) dnssd_close(sdRef->sockfd); +#else dnssd_close(sdRef->sockfd); +#endif + // Free DNSRecords added in DNSRegisterRecord if they have not + // been freed in DNSRemoveRecord while (sdRef) { DNSServiceOp *p = sdRef; @@ -1117,18 +1060,19 @@ static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeade get_string(&data, end, fullname, kDNSServiceMaxDomainName); get_string(&data, end, target, kDNSServiceMaxDomainName); - if (!data || data + 2 > end) data = NULL; - else - { - port.b[0] = *data++; - port.b[1] = *data++; - } + if (!data || data + 2 > end) goto fail; + + port.b[0] = *data++; + port.b[1] = *data++; txtlen = get_uint16(&data, end); txtrecord = (unsigned char *)get_rdata(&data, end, txtlen); - if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon"); - else ((DNSServiceResolveReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, fullname, target, port.s, txtlen, txtrecord, sdr->AppContext); + if (!data) goto fail; + ((DNSServiceResolveReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, fullname, target, port.s, txtlen, txtrecord, sdr->AppContext); + return; // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function +fail: + syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon"); } DNSServiceErrorType DNSSD_API DNSServiceResolve @@ -1375,6 +1319,7 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse return err; } +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain); DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain) { DNSServiceOp *tmp; @@ -1576,6 +1521,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord size_t len; ipc_msg_hdr *hdr = NULL; DNSRecordRef rref = NULL; + DNSRecord **p; int f1 = (flags & kDNSServiceFlagsShared) != 0; int f2 = (flags & kDNSServiceFlagsUnique) != 0; if (f1 + f2 != 1) return kDNSServiceErr_BadParam; @@ -1620,10 +1566,15 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord rref->AppCallback = callBack; rref->record_index = sdRef->max_index++; rref->sdr = sdRef; + rref->recnext = NULL; *RecordRef = rref; hdr->client_context.context = rref; hdr->reg_index = rref->record_index; + p = &(sdRef)->rec; + while (*p) p = &(*p)->recnext; + *p = rref; + return deliver_request(hdr, sdRef); // Will free hdr for us } @@ -1643,6 +1594,7 @@ DNSServiceErrorType DNSSD_API DNSServiceAddRecord size_t len = 0; char *ptr; DNSRecordRef rref; + DNSRecord **p; if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } if (!RecordRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSRecordRef pointer"); return kDNSServiceErr_BadParam; } @@ -1679,9 +1631,14 @@ DNSServiceErrorType DNSSD_API DNSServiceAddRecord rref->AppCallback = NULL; rref->record_index = sdRef->max_index++; rref->sdr = sdRef; + rref->recnext = NULL; *RecordRef = rref; hdr->reg_index = rref->record_index; + p = &(sdRef)->rec; + while (*p) p = &(*p)->recnext; + *p = rref; + return deliver_request(hdr, sdRef); // Will free hdr for us } @@ -1753,7 +1710,15 @@ DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord hdr->reg_index = RecordRef->record_index; put_flags(flags, &ptr); err = deliver_request(hdr, sdRef); // Will free hdr for us - if (!err) free(RecordRef); + if (!err) + { + // This RecordRef could have been allocated in DNSServiceRegisterRecord or DNSServiceAddRecord. + // If so, delink from the list before freeing + DNSRecord **p = &sdRef->rec; + while (*p && *p != RecordRef) p = &(*p)->recnext; + if (*p) *p = RecordRef->recnext; + free(RecordRef); + } return err; } @@ -1800,29 +1765,31 @@ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord static void handle_port_mapping_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end) { union { uint32_t l; u_char b[4]; } addr; - uint8_t protocol = 0; + uint8_t protocol; union { uint16_t s; u_char b[2]; } internalPort; union { uint16_t s; u_char b[2]; } externalPort; - uint32_t ttl = 0; - - if (!data || data + 13 > end) data = NULL; - else - { - addr .b[0] = *data++; - addr .b[1] = *data++; - addr .b[2] = *data++; - addr .b[3] = *data++; - protocol = *data++; - internalPort.b[0] = *data++; - internalPort.b[1] = *data++; - externalPort.b[0] = *data++; - externalPort.b[1] = *data++; - ttl = get_uint32(&data, end); - } + uint32_t ttl; - if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_port_mapping_response: error reading result from daemon"); - else ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, addr.l, protocol, internalPort.s, externalPort.s, ttl, sdr->AppContext); + if (!data || data + 13 > end) goto fail; + + addr .b[0] = *data++; + addr .b[1] = *data++; + addr .b[2] = *data++; + addr .b[3] = *data++; + protocol = *data++; + internalPort.b[0] = *data++; + internalPort.b[1] = *data++; + externalPort.b[0] = *data++; + externalPort.b[1] = *data++; + ttl = get_uint32(&data, end); + if (!data) goto fail; + + ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, addr.l, protocol, internalPort.s, externalPort.s, ttl, sdr->AppContext); + return; // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function + +fail: + syslog(LOG_WARNING, "dnssd_clientstub handle_port_mapping_response: error reading result from daemon"); } DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate @@ -1870,3 +1837,41 @@ DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; } return err; } + +#if _DNS_SD_LIBDISPATCH +DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue + ( + DNSServiceRef service, + dispatch_queue_t queue + ) + { + int dnssd_fd = DNSServiceRefSockFD(service); + if (dnssd_fd == dnssd_InvalidSocket) return kDNSServiceErr_BadParam; + if (!queue) + { + syslog(LOG_WARNING, "dnssd_clientstub: DNSServiceSetDispatchQueue dispatch queue NULL"); + return kDNSServiceErr_BadParam; + } + if (service->disp_queue) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch queue set already"); + return kDNSServiceErr_BadParam; + } + if (service->disp_source) + { + syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch source set already"); + return kDNSServiceErr_BadParam; + } + service->disp_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, dnssd_fd, 0, queue); + if (!service->disp_source) + { + syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch_source_create failed"); + return kDNSServiceErr_NoMemory; + } + service->disp_queue = queue; + dispatch_source_set_event_handler(service->disp_source, ^{DNSServiceProcessResult(service);}); + dispatch_source_set_cancel_handler(service->disp_source, ^{dnssd_close(dnssd_fd);}); + dispatch_resume(service->disp_source); + return kDNSServiceErr_NoError; + } +#endif // _DNS_SD_LIBDISPATCH diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_ipc.h b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_ipc.h index f65674326c0..aa5b8473b0b 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_ipc.h +++ b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_ipc.h @@ -24,138 +24,6 @@ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Change History (most recent first): - -Log: dnssd_ipc.h,v $ -Revision 1.46 2009/05/27 22:20:44 cheshire -Removed unused dnssd_errno_assign() (we have no business writing to errno -- we should only read it) - -Revision 1.45 2009/05/26 21:31:07 herscher -Fix compile errors on Windows - -Revision 1.44 2009/02/12 20:28:31 cheshire -Added some missing "const" declarations - -Revision 1.43 2008/10/23 23:21:31 cheshire -Moved definition of dnssd_strerror() to be with the definition of dnssd_errno, in dnssd_ipc.h - -Revision 1.42 2008/10/23 23:06:17 cheshire -Removed () from dnssd_errno macro definition -- it's not a function and doesn't need any arguments - -Revision 1.41 2008/09/27 01:04:09 cheshire -Added "send_bpf" to list of request_op_t operation codes - -Revision 1.40 2007/09/07 20:56:03 cheshire -Renamed uint32_t field in client_context_t from "ptr64" to more accurate name "u32" - -Revision 1.39 2007/08/18 01:02:04 mcguire -<rdar://problem/5415593> No Bonjour services are getting registered at boot - -Revision 1.38 2007/08/08 22:34:59 mcguire -<rdar://problem/5197869> Security: Run mDNSResponder as user id mdnsresponder instead of root - -Revision 1.37 2007/07/28 00:00:43 cheshire -Renamed CompileTimeAssertionCheck structure for consistency with others - -Revision 1.36 2007/07/23 22:12:53 cheshire -<rdar://problem/5352299> Make mDNSResponder more defensive against malicious local clients - -Revision 1.35 2007/05/23 18:59:22 cheshire -Remove unnecessary IPC_FLAGS_REUSE_SOCKET - -Revision 1.34 2007/05/22 01:07:42 cheshire -<rdar://problem/3563675> API: Need a way to get version/feature information - -Revision 1.33 2007/05/18 23:55:22 cheshire -<rdar://problem/4454655> Allow multiple register/browse/resolve operations to share single Unix Domain Socket - -Revision 1.32 2007/05/18 20:31:20 cheshire -Rename port_mapping_create_request to port_mapping_request - -Revision 1.31 2007/05/18 17:56:20 cheshire -Rename port_mapping_create_reply_op to port_mapping_reply_op - -Revision 1.30 2007/05/16 01:06:52 cheshire -<rdar://problem/4471320> Improve reliability of kDNSServiceFlagsMoreComing flag on multiprocessor machines - -Revision 1.29 2007/05/15 21:57:16 cheshire -<rdar://problem/4608220> Use dnssd_SocketValid(x) macro instead of just -assuming that all negative values (or zero!) are invalid socket numbers - -Revision 1.28 2007/03/21 19:01:57 cheshire -<rdar://problem/5078494> IPC code not 64-bit-savvy: assumes long=32bits, and short=16bits - -Revision 1.27 2006/10/27 00:38:22 cheshire -Strip accidental trailing whitespace from lines - -Revision 1.26 2006/09/27 00:44:36 herscher -<rdar://problem/4249761> API: Need DNSServiceGetAddrInfo() - -Revision 1.25 2006/09/26 01:51:07 herscher -<rdar://problem/4245016> NAT Port Mapping API (for both NAT-PMP and UPnP Gateway Protocol) - -Revision 1.24 2006/09/18 19:21:42 cheshire -<rdar://problem/4737048> gcc's structure padding breaks Bonjour APIs on -64-bit clients; need to declare ipc_msg_hdr structure "packed" - -Revision 1.23 2006/08/14 23:05:53 cheshire -Added "tab-width" emacs header line - -Revision 1.22 2006/06/28 08:56:26 cheshire -Added "_op" to the end of the operation code enum values, -to differentiate them from the routines with the same names - -Revision 1.21 2005/09/29 06:38:13 herscher -Remove #define MSG_WAITALL on Windows. We don't use this macro anymore, and it's presence causes warnings to be emitted when compiling against the latest Microsoft Platform SDK. - -Revision 1.20 2005/03/21 00:39:31 shersche -<rdar://problem/4021486> Fix build warnings on Win32 platform - -Revision 1.19 2005/02/02 02:25:22 cheshire -<rdar://problem/3980388> /var/run/mDNSResponder should be /var/run/mdnsd on Linux - -Revision 1.18 2005/01/27 22:57:56 cheshire -Fix compile errors on gcc4 - -Revision 1.17 2004/11/23 03:39:47 cheshire -Let interface name/index mapping capability live directly in JNISupport.c, -instead of having to call through to the daemon via IPC to get this information. - -Revision 1.16 2004/11/12 03:21:41 rpantos -rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. - -Revision 1.15 2004/10/06 02:22:20 cheshire -Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" - -Revision 1.14 2004/10/01 22:15:55 rpantos -rdar://problem/3824265: Replace APSL in client lib with BSD license. - -Revision 1.13 2004/09/16 23:14:25 cheshire -Changes for Windows compatibility - -Revision 1.12 2004/09/16 21:46:38 ksekar -<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain - -Revision 1.11 2004/08/10 06:24:56 cheshire -Use types with precisely defined sizes for 'op' and 'reg_index', for better -compatibility if the daemon and the client stub are built using different compilers - -Revision 1.10 2004/07/07 17:39:25 shersche -Change MDNS_SERVERPORT from 5533 to 5354. - -Revision 1.9 2004/06/25 00:26:27 rpantos -Changes to fix the Posix build on Solaris. - -Revision 1.8 2004/06/18 04:56:51 rpantos -Add layer for platform code - -Revision 1.7 2004/06/12 01:08:14 cheshire -Changes for Windows compatibility - -Revision 1.6 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - */ #ifndef DNSSD_IPC_H @@ -179,6 +47,8 @@ Update to APSL 2.0 # define dnssd_strerror(X) win32_strerror(X) # define ssize_t int # define getpid _getpid +# define unlink _unlink +extern char *win32_strerror(int inErrorCode); #else # include <sys/types.h> # include <unistd.h> @@ -249,7 +119,7 @@ Update to APSL 2.0 typedef enum { request_op_none = 0, // No request yet received on this connection - connection_request = 1, // connected socket via DNSServiceConnect() + connection_request = 1, // connected socket via DNSServiceCreateConnection() reg_record_request, // reg/remove record only valid for connected sockets remove_record_request, enumeration_request, @@ -303,7 +173,7 @@ typedef packedstruct uint32_t op; // request_op_t or reply_op_t client_context_t client_context; // context passed from client, returned by server in corresponding reply uint32_t reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a - // socket connected by DNSServiceConnect(). Must be unique in the scope of the connection, such that and + // socket connected by DNSServiceCreateConnection(). Must be unique in the scope of the connection, such that and // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord()) } ipc_msg_hdr; diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.c b/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.c index d46f20e1564..ba46109c1b5 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.c +++ b/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.c @@ -13,876 +13,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - - Change History (most recent first): - -Log: uds_daemon.c,v $ -Revision 1.461 2009/06/19 23:15:07 cheshire -<rdar://problem/6990066> Library: crash at handle_resolve_response + 183 -Made resolve_result_callback code more defensive and improved LogOperation messages - -Revision 1.460 2009/05/26 21:31:07 herscher -Fix compile errors on Windows - -Revision 1.459 2009/04/30 20:07:51 mcguire -<rdar://problem/6822674> Support multiple UDSs from launchd - -Revision 1.458 2009/04/25 00:59:06 mcguire -Change a few stray LogInfo to LogOperation - -Revision 1.457 2009/04/22 01:19:57 jessic2 -<rdar://problem/6814585> Daemon: mDNSResponder is logging garbage for error codes because it's using %ld for int 32 - -Revision 1.456 2009/04/21 01:56:34 jessic2 -<rdar://problem/6803941> BTMM: Back out change for preventing other local users from sending packets to your BTMM machines - -Revision 1.455 2009/04/20 19:19:57 cheshire -<rdar://problem/6803941> BTMM: If multiple local users are logged in to same BTMM account, all but one fail -Don't need "empty info->u.browser.browsers list" debugging message, now that we expect this to be -a case that can legitimately happen. - -Revision 1.454 2009/04/18 20:56:43 jessic2 -<rdar://problem/6803941> BTMM: If multiple local users are logged in to same BTMM account, all but one fail - -Revision 1.453 2009/04/11 00:20:29 jessic2 -<rdar://problem/4426780> Daemon: Should be able to turn on LogOperation dynamically - -Revision 1.452 2009/04/07 01:17:42 jessic2 -<rdar://problem/6747917> BTMM: Multiple accounts lets me see others' remote services & send packets to others' remote hosts - -Revision 1.451 2009/04/02 22:34:26 jessic2 -<rdar://problem/6305347> Race condition: If fd has already been closed, SO_NOSIGPIPE returns errno 22 (Invalid argument) - -Revision 1.450 2009/04/01 21:11:28 herscher -<rdar://problem/5925472> Current Bonjour code does not compile on Windows. Workaround use of recvmsg. - -Revision 1.449 2009/03/17 19:44:25 cheshire -<rdar://problem/6688927> Don't let negative unicast answers block Multicast DNS responses - -Revision 1.448 2009/03/17 04:53:40 cheshire -<rdar://problem/6688927> Don't let negative unicast answers block Multicast DNS responses - -Revision 1.447 2009/03/17 04:41:32 cheshire -Moved LogOperation message to after check for "if (answer->RecordType == kDNSRecordTypePacketNegative)" - -Revision 1.446 2009/03/04 01:47:35 cheshire -Include m->ProxyRecords in SIGINFO output - -Revision 1.445 2009/03/03 23:04:44 cheshire -For clarity, renamed "MAC" field to "HMAC" (Host MAC, as opposed to Interface MAC) - -Revision 1.444 2009/03/03 22:51:55 cheshire -<rdar://problem/6504236> Sleep Proxy: Waking on same network but different interface will cause conflicts - -Revision 1.443 2009/02/27 02:28:41 cheshire -Need to declare "const AuthRecord *ar;" - -Revision 1.442 2009/02/27 00:58:17 cheshire -Improved detail of SIGINFO logging for m->DuplicateRecords - -Revision 1.441 2009/02/24 22:18:59 cheshire -Include interface name for interface-specific AuthRecords - -Revision 1.440 2009/02/21 01:38:08 cheshire -Added report of m->SleepState value in SIGINFO output - -Revision 1.439 2009/02/18 23:38:44 cheshire -<rdar://problem/6600780> Could not write data to client 13 - aborting connection -Eliminated unnecessary "request_state *request" field from the reply_state structure. - -Revision 1.438 2009/02/18 23:23:14 cheshire -Cleaned up debugging log messages - -Revision 1.437 2009/02/17 23:29:05 cheshire -Throttle logging to a slower rate when running on SnowLeopard - -Revision 1.436 2009/02/13 06:28:02 cheshire -Converted LogOperation messages to LogInfo - -Revision 1.435 2009/02/12 20:57:26 cheshire -Renamed 'LogAllOperation' switch to 'LogClientOperations'; added new 'LogSleepProxyActions' switch - -Revision 1.434 2009/02/12 20:28:31 cheshire -Added some missing "const" declarations - -Revision 1.433 2009/02/10 01:44:39 cheshire -<rdar://problem/6553729> DNSServiceUpdateRecord fails with kDNSServiceErr_BadReference for otherwise valid reference - -Revision 1.432 2009/02/10 01:38:56 cheshire -Move regservice_termination_callback() earlier in file in preparation for subsequent work - -Revision 1.431 2009/02/07 01:48:55 cheshire -In SIGINFO output include sequence number for proxied records - -Revision 1.430 2009/01/31 21:58:05 cheshire -<rdar://problem/4786302> Implement logic to determine when to send dot-local lookups via Unicast -Only want to do unicast dot-local lookups for address queries and conventional (RFC 2782) SRV queries - -Revision 1.429 2009/01/31 00:45:26 cheshire -<rdar://problem/4786302> Implement logic to determine when to send dot-local lookups via Unicast -Further refinements - -Revision 1.428 2009/01/30 19:52:31 cheshire -Eliminated unnecessary duplicated "dnssd_sock_t sd" fields in service_instance and reply_state structures - -Revision 1.427 2009/01/24 01:48:43 cheshire -<rdar://problem/4786302> Implement logic to determine when to send dot-local lookups via Unicast - -Revision 1.426 2009/01/16 21:07:08 cheshire -In SIGINFO "Duplicate Records" list, show expiry time for Sleep Proxy records - -Revision 1.425 2009/01/16 20:53:16 cheshire -Include information about Sleep Proxy records in SIGINFO output - -Revision 1.424 2009/01/12 22:43:50 cheshire -Fixed "unused variable" warning when SO_NOSIGPIPE is not defined - -Revision 1.423 2009/01/10 22:54:42 mkrochma -<rdar://problem/5797544> Fixes from Igor Seleznev to get mdnsd working on Linux - -Revision 1.422 2009/01/10 01:52:48 cheshire -Include DuplicateRecords and LocalOnlyQuestions in SIGINFO output - -Revision 1.421 2008/12/17 05:05:26 cheshire -Fixed alignment of NAT mapping syslog messages - -Revision 1.420 2008/12/12 00:52:05 cheshire -mDNSPlatformSetBPF is now called mDNSPlatformReceiveBPF_fd - -Revision 1.419 2008/12/10 02:11:44 cheshire -ARMv5 compiler doesn't like uncommented stuff after #endif - -Revision 1.418 2008/12/09 05:12:53 cheshire -Updated debugging messages - -Revision 1.417 2008/12/04 03:38:12 cheshire -Miscellaneous defensive coding changes and improvements to debugging log messages - -Revision 1.416 2008/12/02 22:02:12 cheshire -<rdar://problem/6320621> Adding domains after TXT record updates registers stale TXT record data - -Revision 1.415 2008/11/26 20:35:59 cheshire -Changed some "LogOperation" debugging messages to "debugf" - -Revision 1.414 2008/11/26 00:02:25 cheshire -Improved SIGINFO output to list AutoBrowseDomains and AutoRegistrationDomains - -Revision 1.413 2008/11/25 04:48:58 cheshire -Added logging to show whether Sleep Proxy Service is active - -Revision 1.412 2008/11/24 23:05:43 cheshire -Additional checking in uds_validatelists() - -Revision 1.411 2008/11/05 21:41:39 cheshire -Updated LogOperation message - -Revision 1.410 2008/11/04 20:06:20 cheshire -<rdar://problem/6186231> Change MAX_DOMAIN_NAME to 256 - -Revision 1.409 2008/10/31 23:44:22 cheshire -Fixed compile error in Posix build - -Revision 1.408 2008/10/29 21:32:33 cheshire -Align "DNSServiceEnumerateDomains ... RESULT" log messages - -Revision 1.407 2008/10/27 07:34:36 cheshire -Additional sanity checks for debugging - -Revision 1.406 2008/10/23 23:55:56 cheshire -Fixed some missing "const" declarations - -Revision 1.405 2008/10/23 23:21:31 cheshire -Moved definition of dnssd_strerror() to be with the definition of dnssd_errno, in dnssd_ipc.h - -Revision 1.404 2008/10/23 23:06:17 cheshire -Removed () from dnssd_errno macro definition -- it's not a function and doesn't need any arguments - -Revision 1.403 2008/10/23 22:33:25 cheshire -Changed "NOTE:" to "Note:" so that BBEdit 9 stops putting those comment lines into the funtion popup menu - -Revision 1.402 2008/10/22 19:47:59 cheshire -Instead of SameRData(), use equivalent IdenticalSameNameRecord() macro - -Revision 1.401 2008/10/22 17:20:40 cheshire -Don't give up if setsockopt SO_NOSIGPIPE fails - -Revision 1.400 2008/10/21 01:06:57 cheshire -Pass BPF fd to mDNSMacOSX.c using mDNSPlatformSetBPF() instead of just writing it into a shared global variable - -Revision 1.399 2008/10/20 22:06:42 cheshire -Updated debugging log messages - -Revision 1.398 2008/10/03 18:25:17 cheshire -Instead of calling "m->MainCallback" function pointer directly, call mDNSCore routine "mDNS_ConfigChanged(m);" - -Revision 1.397 2008/10/02 22:26:21 cheshire -Moved declaration of BPF_fd from uds_daemon.c to mDNSMacOSX.c, where it really belongs - -Revision 1.396 2008/09/30 01:04:55 cheshire -Made BPF code a bit more defensive, to ignore subsequent BPF fds if we get passed more than one - -Revision 1.395 2008/09/27 01:28:43 cheshire -Added code to receive and store BPF fd when passed via a send_bpf message - -Revision 1.394 2008/09/23 04:12:40 cheshire -<rdar://problem/6238774> Remove "local" from the end of _services._dns-sd._udp PTR records -Added a special-case to massage these new records for Bonjour Browser's benefit - -Revision 1.393 2008/09/23 03:01:58 cheshire -Added operation logging of domain enumeration results - -Revision 1.392 2008/09/18 22:30:06 cheshire -<rdar://problem/6230679> device-info record not removed when last service deregisters - -Revision 1.391 2008/09/18 22:05:44 cheshire -Fixed "DNSServiceRegister ... ADDED" message to have escaping consistent with -the other DNSServiceRegister operation messages - -Revision 1.390 2008/09/16 21:06:56 cheshire -Improved syslog output to show if q->LongLived flag is set for multicast questions - -Revision 1.389 2008/07/25 22:34:11 mcguire -fix sizecheck issues for 64bit - -Revision 1.388 2008/07/01 01:40:02 mcguire -<rdar://problem/5823010> 64-bit fixes - -Revision 1.387 2008/02/26 21:24:13 cheshire -Fixed spelling mistake in comment - -Revision 1.386 2008/02/26 20:23:15 cheshire -Updated comments - -Revision 1.385 2008/02/19 21:50:52 cheshire -Shortened some overly-long lines - -Revision 1.384 2007/12/22 01:38:05 cheshire -Improve display of "Auth Records" SIGINFO output - -Revision 1.383 2007/12/07 00:45:58 cheshire -<rdar://problem/5526800> BTMM: Need to deregister records and services on shutdown/sleep - -Revision 1.382 2007/11/30 20:11:48 cheshire -Fixed compile warning: declaration of 'remove' shadows a global declaration - -Revision 1.381 2007/11/28 22:02:52 cheshire -Remove pointless "if (!domain)" check (domain is an array on the stack, so its address can never be null) - -Revision 1.380 2007/11/28 18:38:41 cheshire -Fixed typo in log message: "DNSServiceResolver" -> "DNSServiceResolve" - -Revision 1.379 2007/11/01 19:32:14 cheshire -Added "DEBUG_64BIT_SCM_RIGHTS" debugging code - -Revision 1.378 2007/10/31 19:21:40 cheshire -Don't show Expire time for records and services that aren't currently registered - -Revision 1.377 2007/10/30 23:48:20 cheshire -Improved SIGINFO listing of question state - -Revision 1.376 2007/10/30 20:43:54 cheshire -Fixed compiler warning when LogClientOperations is turned off - -Revision 1.375 2007/10/26 22:51:38 cheshire -Improved SIGINFO output to show timers for AuthRecords and ServiceRegistrations - -Revision 1.374 2007/10/25 22:45:02 cheshire -Tidied up code for DNSServiceRegister callback status messages - -Revision 1.373 2007/10/25 21:28:43 cheshire -Add ServiceRegistrations to SIGINFO output - -Revision 1.372 2007/10/25 21:21:45 cheshire -<rdar://problem/5496734> BTMM: Need to retry registrations after failures -Don't unlink_and_free_service_instance at the first error - -Revision 1.371 2007/10/18 23:34:40 cheshire -<rdar://problem/5532821> Need "considerable burden on the network" warning in uds_daemon.c - -Revision 1.370 2007/10/17 18:44:23 cheshire -<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown - -Revision 1.369 2007/10/16 17:18:27 cheshire -Fixed Posix compile errors - -Revision 1.368 2007/10/16 16:58:58 cheshire -Improved debugging error messages in read_msg() - -Revision 1.367 2007/10/15 22:55:14 cheshire -Make read_msg return "void" (since request_callback just ignores the redundant return value anyway) - -Revision 1.366 2007/10/10 00:48:54 cheshire -<rdar://problem/5526379> Daemon spins in an infinite loop when it doesn't get the control message it's expecting - -Revision 1.365 2007/10/06 03:25:23 cheshire -<rdar://problem/5525267> MacBuddy exits abnormally when clicking "Continue" in AppleConnect pane - -Revision 1.364 2007/10/06 03:20:16 cheshire -Improved LogOperation debugging messages - -Revision 1.363 2007/10/05 23:24:52 cheshire -Improved LogOperation messages about separate error return socket - -Revision 1.362 2007/10/05 22:11:58 cheshire -Improved "send_msg ERROR" debugging message - -Revision 1.361 2007/10/04 20:45:18 cheshire -<rdar://problem/5518381> Race condition in kDNSServiceFlagsShareConnection-mode call handling - -Revision 1.360 2007/10/01 23:24:46 cheshire -SIGINFO output was mislabeling mDNSInterface_Any queries as unicast queries - -Revision 1.359 2007/09/30 00:09:27 cheshire -<rdar://problem/5492315> Pass socket fd via SCM_RIGHTS sendmsg instead of using named UDS in the filesystem - -Revision 1.358 2007/09/29 20:08:06 cheshire -Fixed typo in comment - -Revision 1.357 2007/09/27 22:10:04 cheshire -Add LogOperation line for DNSServiceRegisterRecord callbacks - -Revision 1.356 2007/09/26 21:29:30 cheshire -Improved question list SIGINFO output - -Revision 1.355 2007/09/26 01:54:34 mcguire -Debugging: In SIGINFO output, show ClientTunnel query interval, which is how we determine whether a query is still active - -Revision 1.354 2007/09/26 01:26:31 cheshire -<rdar://problem/5501567> BTMM: mDNSResponder crashes in free_service_instance enabling/disabling BTMM -Need to call SendServiceRemovalNotification *before* backpointer is cleared - -Revision 1.353 2007/09/25 20:46:33 cheshire -Include DNSServiceRegisterRecord operations in SIGINFO output - -Revision 1.352 2007/09/25 20:23:40 cheshire -<rdar://problem/5501567> BTMM: mDNSResponder crashes in free_service_instance enabling/disabling BTMM -Need to clear si->request backpointer before calling mDNS_DeregisterService(&mDNSStorage, &si->srs); - -Revision 1.351 2007/09/25 18:20:34 cheshire -Changed name of "free_service_instance" to more accurate "unlink_and_free_service_instance" - -Revision 1.350 2007/09/24 23:54:52 mcguire -Additional list checking in uds_validatelists() - -Revision 1.349 2007/09/24 06:01:00 cheshire -Debugging: In SIGINFO output, show NAT Traversal time values in seconds rather than platform ticks - -Revision 1.348 2007/09/24 05:02:41 cheshire -Debugging: In SIGINFO output, indicate explicitly when a given section is empty - -Revision 1.347 2007/09/21 02:04:33 cheshire -<rdar://problem/5440831> BTMM: mDNSResponder crashes in free_service_instance enabling/disabling BTMM - -Revision 1.346 2007/09/19 22:47:25 cheshire -<rdar://problem/5490182> Memory corruption freeing a "no such service" service record - -Revision 1.345 2007/09/19 20:32:29 cheshire -<rdar://problem/5482322> BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6 - -Revision 1.344 2007/09/19 19:27:50 cheshire -<rdar://problem/5492182> Improved diagnostics when daemon can't connect to error return path socket - -Revision 1.343 2007/09/18 21:42:30 cheshire -To reduce programming mistakes, renamed ExtPort to RequestedPort - -Revision 1.342 2007/09/14 22:38:20 cheshire -Additional list checking in uds_validatelists() - -Revision 1.341 2007/09/13 00:16:43 cheshire -<rdar://problem/5468706> Miscellaneous NAT Traversal improvements - -Revision 1.340 2007/09/12 23:03:08 cheshire -<rdar://problem/5476978> DNSServiceNATPortMappingCreate callback not giving correct interface index - -Revision 1.339 2007/09/12 19:22:21 cheshire -Variable renaming in preparation for upcoming fixes e.g. priv/pub renamed to intport/extport -Made NAT Traversal packet handlers take typed data instead of anonymous "mDNSu8 *" byte pointers - -Revision 1.338 2007/09/12 01:22:13 cheshire -Improve validatelists() checking to detect when 'next' pointer gets smashed to ~0 - -Revision 1.337 2007/09/07 23:05:04 cheshire -Add display of client_context field in handle_cancel_request() LogOperation message -While loop was checking client_context.u32[2] instead of client_context.u32[1] - -Revision 1.336 2007/09/07 20:56:03 cheshire -Renamed uint32_t field in client_context_t from "ptr64" to more accurate name "u32" - -Revision 1.335 2007/09/05 22:25:01 vazquez -<rdar://problem/5400521> update_record mDNSResponder leak - -Revision 1.334 2007/09/05 20:43:57 cheshire -Added LogOperation message showing fd of socket listening for incoming Unix Domain Socket client requests - -Revision 1.333 2007/08/28 23:32:35 cheshire -Added LogOperation messages for DNSServiceNATPortMappingCreate() operations - -Revision 1.332 2007/08/27 22:59:31 cheshire -Show reg_index in DNSServiceRegisterRecord/DNSServiceRemoveRecord messages - -Revision 1.331 2007/08/27 20:29:57 cheshire -Added SIGINFO listing of TunnelClients - -Revision 1.330 2007/08/24 23:46:50 cheshire -Added debugging messages and SIGINFO listing of DomainAuthInfo records - -Revision 1.329 2007/08/18 01:02:04 mcguire -<rdar://problem/5415593> No Bonjour services are getting registered at boot - -Revision 1.328 2007/08/15 20:18:28 vazquez -<rdar://problem/5400521> update_record mDNSResponder leak -Make sure we free all ExtraResourceRecords - -Revision 1.327 2007/08/08 22:34:59 mcguire -<rdar://problem/5197869> Security: Run mDNSResponder as user id mdnsresponder instead of root - -Revision 1.326 2007/08/01 16:09:14 cheshire -Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly - -Revision 1.325 2007/07/31 21:29:41 cheshire -<rdar://problem/5372207> System Default registration domain(s) not listed in Domain Enumeration ("dns-sd -E") - -Revision 1.324 2007/07/31 01:56:21 cheshire -Corrected function name in log message - -Revision 1.323 2007/07/27 23:57:23 cheshire -Added compile-time structure size checks - -Revision 1.322 2007/07/27 19:37:19 cheshire -Moved AutomaticBrowseDomainQ into main mDNS object - -Revision 1.321 2007/07/27 19:30:41 cheshire -Changed mDNSQuestionCallback parameter from mDNSBool to QC_result, -to properly reflect tri-state nature of the possible responses - -Revision 1.320 2007/07/27 00:48:27 cheshire -<rdar://problem/4700198> BTMM: Services should only get registered in .Mac domain of current user -<rdar://problem/4731180> BTMM: Only browse in the current user's .Mac domain by default - -Revision 1.319 2007/07/24 17:23:33 cheshire -<rdar://problem/5357133> Add list validation checks for debugging - -Revision 1.318 2007/07/23 23:09:51 cheshire -<rdar://problem/5351997> Reject oversized client requests - -Revision 1.317 2007/07/23 22:24:47 cheshire -<rdar://problem/5352299> Make mDNSResponder more defensive against malicious local clients -Additional refinements - -Revision 1.316 2007/07/23 22:12:53 cheshire -<rdar://problem/5352299> Make mDNSResponder more defensive against malicious local clients - -Revision 1.315 2007/07/21 01:36:13 cheshire -Need to also add ".local" as automatic browsing domain - -Revision 1.314 2007/07/20 20:12:37 cheshire -Rename "mDNS_DomainTypeBrowseLegacy" as "mDNS_DomainTypeBrowseAutomatic" - -Revision 1.313 2007/07/20 00:54:21 cheshire -<rdar://problem/4641118> Need separate SCPreferences for per-user .Mac settings - -Revision 1.312 2007/07/11 03:06:43 cheshire -<rdar://problem/5303807> Register IPv6-only hostname and don't create port mappings for AutoTunnel services - -Revision 1.311 2007/07/06 21:19:18 cheshire -Add list of NAT traversals to SIGINFO output - -Revision 1.310 2007/07/03 19:56:50 cheshire -Add LogOperation message for DNSServiceSetDefaultDomainForUser - -Revision 1.309 2007/06/29 23:12:49 vazquez -<rdar://problem/5294103> Stop using generate_final_fatal_reply_with_garbage - -Revision 1.308 2007/06/29 00:10:07 vazquez -<rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes) - -Revision 1.307 2007/05/25 00:25:44 cheshire -<rdar://problem/5227737> Need to enhance putRData to output all current known types - -Revision 1.306 2007/05/24 22:31:35 vazquez -Bug #: 4272956 -Reviewed by: Stuart Cheshire -<rdar://problem/4272956> WWDC API: Return ADD/REMOVE events in registration callback - -Revision 1.305 2007/05/23 18:59:22 cheshire -Remove unnecessary IPC_FLAGS_REUSE_SOCKET - -Revision 1.304 2007/05/22 01:07:42 cheshire -<rdar://problem/3563675> API: Need a way to get version/feature information - -Revision 1.303 2007/05/22 00:32:58 cheshire -Make a send_all() subroutine -- will be helpful for implementing DNSServiceGetProperty(DaemonVersion) - -Revision 1.302 2007/05/21 18:54:54 cheshire -Add "Cancel" LogOperation message when we get a cancel_request command over the UDS - -Revision 1.301 2007/05/18 23:55:22 cheshire -<rdar://problem/4454655> Allow multiple register/browse/resolve operations to share single Unix Domain Socket - -Revision 1.300 2007/05/18 21:27:11 cheshire -Rename connected_registration_termination to connection_termination - -Revision 1.299 2007/05/18 21:24:34 cheshire -Rename rstate to request - -Revision 1.298 2007/05/18 21:22:35 cheshire -Convert uint16_t etc. to their locally-defined equivalents, like the rest of the core code - -Revision 1.297 2007/05/18 20:33:11 cheshire -Avoid declaring lots of uninitialized variables in read_rr_from_ipc_msg - -Revision 1.296 2007/05/18 19:04:19 cheshire -Rename msgdata to msgptr (may be modified); rename (currently unused) bufsize to msgend - -Revision 1.295 2007/05/18 17:57:13 cheshire -Reorder functions in file to arrange them in logical groups; added "#pragma mark" headers for each group - -Revision 1.294 2007/05/17 20:58:22 cheshire -<rdar://problem/4647145> DNSServiceQueryRecord should return useful information with NXDOMAIN - -Revision 1.293 2007/05/17 19:46:20 cheshire -Routine name deliver_async_error() is misleading. What it actually does is write a message header -(containing an error code) followed by 256 bytes of garbage zeroes onto a client connection, -thereby trashing it and making it useless for any subsequent communication. It's destructive, -and not very useful. Changing name to generate_final_fatal_reply_with_garbage(). - -Revision 1.292 2007/05/16 01:06:52 cheshire -<rdar://problem/4471320> Improve reliability of kDNSServiceFlagsMoreComing flag on multiprocessor machines - -Revision 1.291 2007/05/15 21:57:16 cheshire -<rdar://problem/4608220> Use dnssd_SocketValid(x) macro instead of just -assuming that all negative values (or zero!) are invalid socket numbers - -Revision 1.290 2007/05/10 23:30:57 cheshire -<rdar://problem/4084490> Only one browse gets remove events when disabling browse domain - -Revision 1.289 2007/05/02 22:18:08 cheshire -Renamed NATTraversalInfo_struct context to NATTraversalContext - -Revision 1.288 2007/04/30 21:33:39 cheshire -Fix crash when a callback unregisters a service while the UpdateSRVRecords() loop -is iterating through the m->ServiceRegistrations list - -Revision 1.287 2007/04/27 19:03:22 cheshire -Check q->LongLived not q->llq to tell if a query is LongLived - -Revision 1.286 2007/04/26 16:00:01 cheshire -Show interface number in DNSServiceBrowse RESULT output - -Revision 1.285 2007/04/22 19:03:39 cheshire -Minor code tidying - -Revision 1.284 2007/04/22 06:02:03 cheshire -<rdar://problem/4615977> Query should immediately return failure when no server - -Revision 1.283 2007/04/21 21:47:47 cheshire -<rdar://problem/4376383> Daemon: Add watchdog timer - -Revision 1.282 2007/04/20 21:17:24 cheshire -For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative - -Revision 1.281 2007/04/19 23:25:20 cheshire -Added debugging message - -Revision 1.280 2007/04/17 19:21:29 cheshire -<rdar://problem/5140339> Domain discovery not working over VPN - -Revision 1.279 2007/04/16 21:53:49 cheshire -Improve display of negative cache entries - -Revision 1.278 2007/04/16 20:49:40 cheshire -Fix compile errors for mDNSPosix build - -Revision 1.277 2007/04/05 22:55:36 cheshire -<rdar://problem/5077076> Records are ending up in Lighthouse without expiry information - -Revision 1.276 2007/04/05 19:20:13 cheshire -Non-blocking mode not being set correctly -- was clobbering other flags - -Revision 1.275 2007/04/04 21:21:25 cheshire -<rdar://problem/4546810> Fix crash: In regservice_callback service_instance was being referenced after being freed - -Revision 1.274 2007/04/04 01:30:42 cheshire -<rdar://problem/5075200> DNSServiceAddRecord is failing to advertise NULL record -Add SIGINFO output lising our advertised Authoritative Records - -Revision 1.273 2007/04/04 00:03:27 cheshire -<rdar://problem/5089862> DNSServiceQueryRecord is returning kDNSServiceErr_NoSuchRecord for empty rdata - -Revision 1.272 2007/04/03 20:10:32 cheshire -Show ADD/RMV in DNSServiceQueryRecord log message instead of just "RESULT" - -Revision 1.271 2007/04/03 19:22:32 cheshire -Use mDNSSameIPv4Address (and similar) instead of accessing internal fields directly - -Revision 1.270 2007/03/30 21:55:30 cheshire -Added comments - -Revision 1.269 2007/03/29 01:31:44 cheshire -Faulty logic was incorrectly suppressing some NAT port mapping callbacks - -Revision 1.268 2007/03/29 00:13:58 cheshire -Remove unnecessary fields from service_instance structure: autoname, autorename, allowremotequery, name - -Revision 1.267 2007/03/28 20:59:27 cheshire -<rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr() - -Revision 1.266 2007/03/28 15:56:37 cheshire -<rdar://problem/5085774> Add listing of NAT port mapping and GetAddrInfo requests in SIGINFO output - -Revision 1.265 2007/03/27 22:52:07 cheshire -Fix crash in udsserver_automatic_browse_domain_changed - -Revision 1.264 2007/03/27 00:49:40 cheshire -Should use mallocL, not plain malloc - -Revision 1.263 2007/03/27 00:45:01 cheshire -Removed unnecessary "void *termination_context" pointer - -Revision 1.262 2007/03/27 00:40:43 cheshire -Eliminate resolve_termination_t as a separately-allocated structure, and make it part of the request_state union - -Revision 1.261 2007/03/27 00:29:00 cheshire -Eliminate queryrecord_request data as a separately-allocated structure, and make it part of the request_state union - -Revision 1.260 2007/03/27 00:18:42 cheshire -Eliminate enum_termination_t and domain_enum_t as separately-allocated structures, -and make them part of the request_state union - -Revision 1.259 2007/03/26 23:48:16 cheshire -<rdar://problem/4848295> Advertise model information via Bonjour -Refinements to reduce unnecessary transmissions of the DeviceInfo TXT record - -Revision 1.258 2007/03/24 00:40:04 cheshire -Minor code cleanup - -Revision 1.257 2007/03/24 00:23:12 cheshire -Eliminate port_mapping_info_t as a separately-allocated structure, and make it part of the request_state union - -Revision 1.256 2007/03/24 00:07:18 cheshire -Eliminate addrinfo_info_t as a separately-allocated structure, and make it part of the request_state union - -Revision 1.255 2007/03/23 23:56:14 cheshire -Move list of record registrations into the request_state union - -Revision 1.254 2007/03/23 23:48:56 cheshire -Eliminate service_info as a separately-allocated structure, and make it part of the request_state union - -Revision 1.253 2007/03/23 23:04:29 cheshire -Eliminate browser_info_t as a separately-allocated structure, and make it part of request_state - -Revision 1.252 2007/03/23 22:59:58 cheshire -<rdar://problem/4848295> Advertise model information via Bonjour -Use kStandardTTL, not kHostNameTTL - -Revision 1.251 2007/03/23 22:44:07 cheshire -Instead of calling AbortUnlinkAndFree() haphazardly all over the place, make the handle* routines -return an error code, and then request_callback() does all necessary cleanup in one place. - -Revision 1.250 2007/03/22 20:30:07 cheshire -Remove pointless "if (request->ts != t_complete) ..." checks - -Revision 1.249 2007/03/22 20:13:27 cheshire -Delete unused client_context field - -Revision 1.248 2007/03/22 20:03:37 cheshire -Rename variables for clarity: instead of using variable rs for both request_state -and reply_state, use req for request_state and rep for reply_state - -Revision 1.247 2007/03/22 19:31:42 cheshire -<rdar://problem/4848295> Advertise model information via Bonjour -Add missing "model=" at start of DeviceInfo data - -Revision 1.246 2007/03/22 18:31:48 cheshire -Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy - -Revision 1.245 2007/03/22 00:49:20 cheshire -<rdar://problem/4848295> Advertise model information via Bonjour - -Revision 1.244 2007/03/21 21:01:48 cheshire -<rdar://problem/4789793> Leak on error path in regrecord_callback, uds_daemon.c - -Revision 1.243 2007/03/21 19:01:57 cheshire -<rdar://problem/5078494> IPC code not 64-bit-savvy: assumes long=32bits, and short=16bits - -Revision 1.242 2007/03/21 18:51:21 cheshire -<rdar://problem/4549320> Code in uds_daemon.c passes function name instead of type name to mallocL/freeL - -Revision 1.241 2007/03/20 00:04:50 cheshire -<rdar://problem/4837929> Should allow "udp" or "tcp" for protocol command-line arg -Fix LogOperation("DNSServiceNATPortMappingCreate(...)") message to actually show client arguments - -Revision 1.240 2007/03/16 23:25:35 cheshire -<rdar://problem/5067001> NAT-PMP: Parameter validation not working correctly - -Revision 1.239 2007/03/10 02:29:36 cheshire -Added comment about port_mapping_create_reply() - -Revision 1.238 2007/03/07 00:26:48 cheshire -<rdar://problem/4426754> DNSServiceRemoveRecord log message should include record type - -Revision 1.237 2007/02/28 01:44:29 cheshire -<rdar://problem/5027863> Byte order bugs in uDNS.c, uds_daemon.c, dnssd_clientstub.c - -Revision 1.236 2007/02/14 01:58:19 cheshire -<rdar://problem/4995831> Don't delete Unix Domain Socket on exit if we didn't create it on startup - -Revision 1.235 2007/02/08 21:12:28 cheshire -<rdar://problem/4386497> Stop reading /etc/mDNSResponder.conf on every sleep/wake - -Revision 1.234 2007/02/06 19:06:49 cheshire -<rdar://problem/3956518> Need to go native with launchd - -Revision 1.233 2007/01/10 20:49:37 cheshire -Remove unnecessary setting of q->Private fields - -Revision 1.232 2007/01/09 00:03:23 cheshire -Call udsserver_handle_configchange() once at the end of udsserver_init() -to set up the automatic registration and browsing domains. - -Revision 1.231 2007/01/06 02:50:19 cheshire -<rdar://problem/4632919> Instead of copying SRV and TXT record data, just store pointers to cache entities - -Revision 1.230 2007/01/06 01:00:35 cheshire -Improved SIGINFO output - -Revision 1.229 2007/01/05 08:30:56 cheshire -Trim excessive "Log" checkin history from before 2006 -(checkin history still available via "cvs log ..." of course) - -Revision 1.228 2007/01/05 08:09:05 cheshire -Reorder code into functional sections, with "#pragma mark" headers - -Revision 1.227 2007/01/05 07:04:24 cheshire -Minor code tidying - -Revision 1.226 2007/01/05 05:44:35 cheshire -Move automatic browse/registration management from uDNS.c to mDNSShared/uds_daemon.c, -so that mDNSPosix embedded clients will compile again - -Revision 1.225 2007/01/04 23:11:15 cheshire -<rdar://problem/4720673> uDNS: Need to start caching unicast records -When an automatic browsing domain is removed, generate appropriate "remove" events for legacy queries - -Revision 1.224 2007/01/04 20:57:49 cheshire -Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates) - -Revision 1.223 2006/12/21 01:25:49 cheshire -Tidy up SIGINFO state log - -Revision 1.222 2006/12/21 00:15:22 cheshire -Get rid of gmDNS macro; fixed a crash in udsserver_info() - -Revision 1.221 2006/12/20 04:07:38 cheshire -Remove uDNS_info substructure from AuthRecord_struct - -Revision 1.220 2006/12/19 22:49:25 cheshire -Remove uDNS_info substructure from ServiceRecordSet_struct - -Revision 1.219 2006/12/14 03:02:38 cheshire -<rdar://problem/4838433> Tools: dns-sd -G 0 only returns IPv6 when you have a routable IPv6 address - -Revision 1.218 2006/11/18 05:01:33 cheshire -Preliminary support for unifying the uDNS and mDNS code, -including caching of uDNS answers - -Revision 1.217 2006/11/15 19:27:53 mkrochma -<rdar://problem/4838433> Tools: dns-sd -G 0 only returns IPv6 when you have a routable IPv6 address - -Revision 1.216 2006/11/10 00:54:16 cheshire -<rdar://problem/4816598> Changing case of Computer Name doesn't work - -Revision 1.215 2006/10/27 01:30:23 cheshire -Need explicitly to set ReturnIntermed = mDNSfalse - -Revision 1.214 2006/10/20 05:37:23 herscher -Display question list information in udsserver_info() - -Revision 1.213 2006/10/05 03:54:31 herscher -Remove embedded uDNS_info struct from DNSQuestion_struct - -Revision 1.212 2006/09/30 01:22:35 cheshire -Put back UTF-8 curly quotes in log messages - -Revision 1.211 2006/09/27 00:44:55 herscher -<rdar://problem/4249761> API: Need DNSServiceGetAddrInfo() - -Revision 1.210 2006/09/26 01:52:41 herscher -<rdar://problem/4245016> NAT Port Mapping API (for both NAT-PMP and UPnP Gateway Protocol) - -Revision 1.209 2006/09/21 21:34:09 cheshire -<rdar://problem/4100000> Allow empty string name when using kDNSServiceFlagsNoAutoRename - -Revision 1.208 2006/09/21 21:28:24 cheshire -Code cleanup to make it consistent with daemon.c: change rename_on_memfree to renameonmemfree - -Revision 1.207 2006/09/15 21:20:16 cheshire -Remove uDNS_info substructure from mDNS_struct - -Revision 1.206 2006/08/14 23:24:56 cheshire -Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 - -Revision 1.205 2006/07/20 22:07:30 mkrochma -<rdar://problem/4633196> Wide-area browsing is currently broken in TOT -More fixes for uninitialized variables - -Revision 1.204 2006/07/15 02:01:33 cheshire -<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder -Fix broken "empty string" browsing - -Revision 1.203 2006/07/07 01:09:13 cheshire -<rdar://problem/4472013> Add Private DNS server functionality to dnsextd -Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsextd - -Revision 1.202 2006/07/05 22:00:10 cheshire -Wide-area cleanup: Rename mDNSPlatformGetRegDomainList() to uDNS_GetDefaultRegDomainList() - -Revision 1.201 2006/06/29 03:02:47 cheshire -<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support - -Revision 1.200 2006/06/28 08:56:26 cheshire -Added "_op" to the end of the operation code enum values, -to differentiate them from the routines with the same names - -Revision 1.199 2006/06/28 08:53:39 cheshire -Added (commented out) debugging messages - -Revision 1.198 2006/06/27 20:16:07 cheshire -Fix code layout - -Revision 1.197 2006/05/18 01:32:35 cheshire -<rdar://problem/4472706> iChat: Lost connection with Bonjour -(mDNSResponder insufficiently defensive against malformed browsing PTR responses) - -Revision 1.196 2006/05/05 07:07:13 cheshire -<rdar://problem/4538206> mDNSResponder fails when UDS reads deliver partial data - -Revision 1.195 2006/04/25 20:56:28 mkrochma -Added comment about previous checkin - -Revision 1.194 2006/04/25 18:29:36 mkrochma -Workaround for warning: unused variable 'status' when building mDNSPosix - -Revision 1.193 2006/03/19 17:14:38 cheshire -<rdar://problem/4483117> Need faster purging of stale records -read_rr_from_ipc_msg was not setting namehash and rdatahash - -Revision 1.192 2006/03/18 20:58:32 cheshire -Misplaced curly brace - -Revision 1.191 2006/03/10 22:19:43 cheshire -Update debugging message in resolve_result_callback() to indicate whether event is ADD or RMV - -Revision 1.190 2006/03/10 21:56:12 cheshire -<rdar://problem/4111464> After record update, old record sometimes remains in cache -When service TXT and SRV record both change, clients with active resolve calls get *two* callbacks, one -when the TXT data changes, and then immediately afterwards a second callback with the new port number -This change suppresses the first unneccessary (and confusing) callback - -Revision 1.189 2006/01/06 00:56:31 cheshire -<rdar://problem/4400573> Should remove PID file on exit - -*/ + */ #if defined(_WIN32) #include <process.h> @@ -912,6 +43,23 @@ Revision 1.189 2006/01/06 00:56:31 cheshire #endif #endif +#if APPLE_OSX_mDNSResponder +#include <WebFilterDNS/WebFilterDNS.h> + +#if ! NO_WCF + +int WCFIsServerRunning(WCFConnection *conn) __attribute__((weak_import)); +int WCFNameResolvesToAddr(WCFConnection *conn, char* domainName, struct sockaddr* address, uid_t userid) __attribute__((weak_import)); +int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid_t userid) __attribute__((weak_import)); + +// Do we really need to define a macro for "if"? +#define CHECK_WCF_FUNCTION(X) if (X) +#endif // ! NO_WCF + +#else +#define NO_WCF 1 +#endif // APPLE_OSX_mDNSResponder + // User IDs 0-500 are system-wide processes, not actual users in the usual sense // User IDs for real user accounts start at 501 and count up from there #define SystemUID(X) ((X) <= 500) @@ -939,9 +87,11 @@ typedef struct registered_record_entry { struct registered_record_entry *next; mDNSu32 key; - AuthRecord *rr; // Pointer to variable-sized AuthRecord client_context_t regrec_client_context; request_state *request; + mDNSBool external_advertise; + mDNSInterfaceID origInterfaceID; + AuthRecord *rr; // Pointer to variable-sized AuthRecord (Why a pointer? Why not just embed it here?) } registered_record_entry; // A single registered service: ServiceRecordSet + bookkeeping @@ -955,6 +105,7 @@ typedef struct service_instance mDNSBool renameonmemfree; // Set on config change when we deregister original name mDNSBool clientnotified; // Has client been notified of successful registration yet? mDNSBool default_local; // is this the "local." from an empty-string registration? + mDNSBool external_advertise; // is this is being advertised externally? domainname domain; ServiceRecordSet srs; // note -- variable-sized object -- must be last field in struct } service_instance; @@ -971,10 +122,11 @@ struct request_state { request_state *next; request_state *primary; // If this operation is on a shared socket, pointer to primary - // request_state for the original DNSServiceConnect() operation + // request_state for the original DNSServiceCreateConnection() operation dnssd_sock_t sd; dnssd_sock_t errsd; mDNSu32 uid; + void * platform_data; // Note: On a shared connection these fields in the primary structure, including hdr, are re-used // for each new request. This is because, until we've read the ipc_msg_hdr to find out what the @@ -989,7 +141,8 @@ struct request_state // reply, termination, error, and client context info int no_reply; // don't send asynchronous replies to client - int time_blocked; // record time of a blocked client + mDNSs32 time_blocked; // record time of a blocked client + int unresponsiveness_reports; struct reply_state *replies; // corresponding (active) reply list req_termination_fn terminate; @@ -1054,6 +207,7 @@ struct request_state const ResourceRecord *txt; const ResourceRecord *srv; mDNSs32 ReportTime; + mDNSBool external_advertise; } resolve; } u; }; @@ -1091,12 +245,21 @@ mDNSexport const char ProgramName[] = PROGRAM_NAME; static dnssd_sock_t listenfd = dnssd_InvalidSocket; static request_state *all_requests = NULL; +// Note asymmetry here between registration and browsing. +// For service registrations we only automatically register in domains that explicitly appear in local configuration data +// (so AutoRegistrationDomains could equally well be called SCPrefRegDomains) +// For service browsing we also learn automatic browsing domains from the network, so for that case we have: +// 1. SCPrefBrowseDomains (local configuration data) +// 2. LocalDomainEnumRecords (locally-generated local-only PTR records -- equivalent to slElem->AuthRecs in uDNS.c) +// 3. AutoBrowseDomains, which is populated by tracking add/rmv events in AutomaticBrowseDomainChange, the callback function for our mDNS_GetDomains call. +// By creating and removing our own LocalDomainEnumRecords, we trigger AutomaticBrowseDomainChange callbacks just like domains learned from the network would. + +mDNSexport DNameListElem *AutoRegistrationDomains; // Domains where we automatically register for empty-string registrations + static DNameListElem *SCPrefBrowseDomains; // List of automatic browsing domains read from SCPreferences for "empty string" browsing static ARListElem *LocalDomainEnumRecords; // List of locally-generated PTR records to augment those we learn from the network mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-only PTR records plus records we get from the network -mDNSexport DNameListElem *AutoRegistrationDomains; // Domains where we automatically register for empty-string registrations - #define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee // n get_string() calls w/o buffer overrun // initialization, setup/teardown functions @@ -1139,6 +302,8 @@ mDNSlocal void abort_request(request_state *req) { LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; } // First stop whatever mDNSCore operation we were doing + // If this is actually a shared connection operation, then its req->terminate function will scan + // the all_requests list and terminate any subbordinate operations sharing this file descriptor if (req->terminate) req->terminate(req); if (!dnssd_SocketValid(req->sd)) @@ -1149,7 +314,7 @@ mDNSlocal void abort_request(request_state *req) { if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd); else LogOperation("%3d: Removing FD", req->sd); - udsSupportRemoveFDFromEventLoop(req->sd); // Note: This also closes file descriptor req->sd for us + udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; } while (req->replies) // free pending replies @@ -1195,11 +360,11 @@ mDNSlocal reply_state *create_reply(const reply_op_t op, const size_t datalen, r if (!reply) FatalError("ERROR: malloc"); reply->next = mDNSNULL; - reply->totallen = datalen + sizeof(ipc_msg_hdr); + reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr); reply->nwriten = 0; reply->mhdr->version = VERSION; - reply->mhdr->datalen = datalen; + reply->mhdr->datalen = (mDNSu32)datalen; reply->mhdr->ipc_flags = 0; reply->mhdr->op = op; reply->mhdr->client_context = request->hdr.client_context; @@ -1413,6 +578,58 @@ mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const d // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - +#pragma mark - external helpers +#endif + +mDNSlocal void external_start_advertising_helper(service_instance *const instance) + { + AuthRecord *st = instance->subtypes; + ExtraResourceRecord *e; + int i; + + if (mDNSIPPortIsZero(instance->request->u.servicereg.port)) + { + LogInfo("external_start_advertising_helper: Not registering service with port number zero"); + return; + } + + if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!"); + + for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) + external_start_advertising_service(&st[i].resrec); + + external_start_advertising_service(&instance->srs.RR_PTR.resrec); + external_start_advertising_service(&instance->srs.RR_TXT.resrec); + + for (e = instance->srs.Extras; e; e = e->next) + external_start_advertising_service(&e->r.resrec); + + instance->external_advertise = mDNStrue; + } + +mDNSlocal void external_stop_advertising_helper(service_instance *const instance) + { + AuthRecord *st = instance->subtypes; + ExtraResourceRecord *e; + int i; + + if (!instance->external_advertise) return; + + for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++) + external_start_advertising_service(&st[i].resrec); + + external_stop_advertising_service(&instance->srs.RR_PTR.resrec); + external_stop_advertising_service(&instance->srs.RR_TXT.resrec); + + for (e = instance->srs.Extras; e; e = e->next) + external_stop_advertising_service(&e->r.resrec); + + instance->external_advertise = mDNSfalse; + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - #pragma mark - DNSServiceRegister #endif @@ -1434,6 +651,8 @@ mDNSlocal void unlink_and_free_service_instance(service_instance *srv) { ExtraResourceRecord *e = srv->srs.Extras, *tmp; + external_stop_advertising_helper(srv); + // clear pointers from parent struct if (srv->request) { @@ -1468,16 +687,11 @@ mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs int count = 0; ResourceRecord *r = &srs->RR_SRV.resrec; AuthRecord *rr; - ServiceRecordSet *s; for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !IdenticalSameNameRecord(&rr->resrec, r)) count++; - for (s = m->ServiceRegistrations; s; s = s->uDNS_next) - if (s->state != regState_Unregistered && SameDomainName(s->RR_SRV.resrec.name, r->name) && !IdenticalSameNameRecord(&s->RR_SRV.resrec, r)) - count++; - verbosedebugf("%d peer registrations for %##s", count, r->name->c); return(count); } @@ -1509,16 +723,13 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m { mStatus err; mDNSBool SuppressError = mDNSfalse; - service_instance *instance = srs->ServiceContext; + service_instance *instance; reply_state *rep; - char *fmt = ""; - if (mDNS_LoggingEnabled) - fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegister(%##s, %u) REGISTERED" : - (result == mStatus_MemFree) ? "%3d: DNSServiceRegister(%##s, %u) DEREGISTERED" : - (result == mStatus_NameConflict) ? "%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT" : - "%3d: DNSServiceRegister(%##s, %u) %s %d"; (void)m; // Unused + if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; } + + instance = srs->ServiceContext; if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; } // don't send errors up to client for wide-area, empty-string registrations @@ -1527,8 +738,18 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m !instance->default_local) SuppressError = mDNStrue; - LogOperation(fmt, instance->request ? instance->request->sd : -99, - srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), SuppressError ? "suppressed error" : "CALLBACK", result); + if (mDNS_LoggingEnabled) + { + const char *const fmt = + (result == mStatus_NoError) ? "%s DNSServiceRegister(%##s, %u) REGISTERED" : + (result == mStatus_MemFree) ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED" : + (result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" : + "%s DNSServiceRegister(%##s, %u) %s %d"; + char prefix[16] = "---:"; + if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd); + LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), + SuppressError ? "suppressed error" : "CALLBACK", result); + } if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; } @@ -1548,6 +769,8 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c); else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; } + if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain))) + external_start_advertising_helper(instance); if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0) RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately } @@ -1555,6 +778,7 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m { if (instance->request && instance->renameonmemfree) { + external_stop_advertising_helper(instance); instance->renameonmemfree = 0; err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name); if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err); @@ -1567,6 +791,7 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m { if (instance->request->u.servicereg.autorename) { + external_stop_advertising_helper(instance); if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0) { // On conflict for an autoname service, rename and reregister *all* autoname services @@ -1590,7 +815,7 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m unlink_and_free_service_instance(instance); } } - else + else // Not mStatus_NoError, mStatus_MemFree, or mStatus_NameConflict { if (!SuppressError) { @@ -1607,7 +832,7 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) if (!rr->RecordContext) // parent struct already freed by termination callback { if (result == mStatus_NoError) - LogMsg("Error: regrecord_callback: successful registration of orphaned record"); + LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr)); else { if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result); @@ -1618,14 +843,27 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) { registered_record_entry *re = rr->RecordContext; request_state *request = re->request; - int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType); - reply_state *reply = create_reply(reg_record_reply_op, len, request); - reply->mhdr->client_context = re->regrec_client_context; - reply->rhdr->flags = dnssd_htonl(0); - reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID)); - reply->rhdr->error = dnssd_htonl(result); - - LogOperation("%3d: DNSServiceRegisterRecord(%u) result %d", request->sd, request->hdr.reg_index, result); + + if (mDNS_LoggingEnabled) + { + char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED" : + (result == mStatus_MemFree) ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED" : + (result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" : + "%3d: DNSServiceRegisterRecord(%u %s) %d"; + LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result); + } + + if (result != mStatus_MemFree) + { + int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType); + reply_state *reply = create_reply(reg_record_reply_op, len, request); + reply->mhdr->client_context = re->regrec_client_context; + reply->rhdr->flags = dnssd_htonl(0); + reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID)); + reply->rhdr->error = dnssd_htonl(result); + append_reply(request, reply); + } + if (result) { // unlink from list, free memory @@ -1636,13 +874,26 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) freeL("registered_record_entry AuthRecord regrecord_callback", re->rr); freeL("registered_record_entry regrecord_callback", re); } - append_reply(request, reply); + else + { + if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!"); + if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage))) + { + external_start_advertising_service(&rr->resrec); + re->external_advertise = mDNStrue; + } + } } } mDNSlocal void connection_termination(request_state *request) { + // When terminating a shared connection, we need to scan the all_requests list + // and terminate any subbordinate operations sharing this file descriptor request_state **req = &all_requests; + + LogOperation("%3d: DNSServiceCreateConnection STOP", request->sd); + while (*req) { if ((*req)->primary == request) @@ -1662,8 +913,14 @@ mDNSlocal void connection_termination(request_state *request) while (request->u.reg_recs) { registered_record_entry *ptr = request->u.reg_recs; + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec)); request->u.reg_recs = request->u.reg_recs->next; ptr->rr->RecordContext = NULL; + if (ptr->external_advertise) + { + ptr->external_advertise = mDNSfalse; + external_stop_advertising_service(&ptr->rr->resrec); + } mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us freeL("registered_record_entry/connection_termination", ptr); } @@ -1699,22 +956,26 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) // allocate registration entry, link into list registered_record_entry *re = mallocL("registered_record_entry", sizeof(registered_record_entry)); if (!re) FatalError("ERROR: malloc"); - re->key = request->hdr.reg_index; - re->rr = rr; - re->request = request; + re->key = request->hdr.reg_index; + re->rr = rr; re->regrec_client_context = request->hdr.client_context; - rr->RecordContext = re; - rr->RecordCallback = regrecord_callback; + re->request = request; + re->external_advertise = mDNSfalse; + rr->RecordContext = re; + rr->RecordCallback = regrecord_callback; + re->next = request->u.reg_recs; request->u.reg_recs = re; - + + re->origInterfaceID = rr->resrec.InterfaceID; + if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any; #if 0 if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError); #endif if (rr->resrec.rroriginalttl == 0) rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); - LogOperation("%3d: DNSServiceRegisterRecord(%u %s)", request->sd, request->hdr.reg_index, RRDisplayString(&mDNSStorage, &rr->resrec)); + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec)); err = mDNS_Register(&mDNSStorage, rr); } return(err); @@ -1732,6 +993,8 @@ mDNSlocal void regservice_termination_callback(request_state *request) // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p) LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP", request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port)); + + external_stop_advertising_helper(p); // Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance // We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing @@ -1780,6 +1043,8 @@ mDNSlocal mStatus add_record_to_service(request_state *request, service_instance if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; } extra->ClientID = request->hdr.reg_index; + if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain)))) + external_start_advertising_service(&extra->r.resrec); return result; } @@ -1816,21 +1081,37 @@ mDNSlocal mStatus handle_add_request(request_state *request) return(result); } -mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd) +mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd, mDNSu16 oldrdlen) { + mDNSBool external_advertise = (rr->UpdateContext) ? *((mDNSBool *)rr->UpdateContext) : mDNSfalse; (void)m; // Unused + + // There are three cases. + // + // 1. We have updated the primary TXT record of the service + // 2. We have updated the TXT record that was added to the service using DNSServiceAddRecord + // 3. We have updated the TXT record that was registered using DNSServiceRegisterRecord + // + // external_advertise is set if we have advertised at least once during the initial addition + // of the record in all of the three cases above. We should have checked for InterfaceID/LocalDomain + // checks during the first time and hence we don't do any checks here + if (external_advertise) + { + ResourceRecord ext = rr->resrec; + if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit; + SetNewRData(&ext, oldrd, oldrdlen); + external_stop_advertising_service(&ext); + external_start_advertising_service(&rr->resrec); + } +exit: if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd); } -mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl) +mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl, const mDNSBool *const external_advertise) { - int rdsize; - RData *newrd; mStatus result; - - if (rdlen > sizeof(RDataBody)) rdsize = rdlen; - else rdsize = sizeof(RDataBody); - newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize); + const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize); if (!newrd) FatalError("ERROR: malloc"); newrd->MaxRDLength = (mDNSu16) rdsize; mDNSPlatformMemCopy(&newrd->u, rdata, rdlen); @@ -1839,9 +1120,11 @@ mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; } - + + if (external_advertise) rr->UpdateContext = (void *)external_advertise; + result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback); - if (result) { LogMsg("ERROR: mDNS_Update - %d", result); freeL("RData/update_record", newrd); } + if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); } return result; } @@ -1872,7 +1155,9 @@ mDNSlocal mStatus handle_update_request(request_state *request) { if (reptr->key == hdr->reg_index) { - result = update_record(reptr->rr, rdlen, rdata, ttl); + result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise); + LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", + request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : "<NONE>"); goto end; } } @@ -1894,8 +1179,7 @@ mDNSlocal mStatus handle_update_request(request_state *request) if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_update_request - malloc"); mDNSPlatformMemCopy(request->u.servicereg.txtdata, rdata, rdlen); } - else - request->u.servicereg.txtdata = NULL; + request->u.servicereg.txtlen = rdlen; } // update a record from a service record set @@ -1910,7 +1194,7 @@ mDNSlocal mStatus handle_update_request(request_state *request) } if (!rr) { result = mStatus_BadReferenceErr; goto end; } - result = update_record(rr, rdlen, rdata, ttl); + result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise); if (result && i->default_local) goto end; else result = mStatus_NoError; // suppress non-local default errors } @@ -1935,14 +1219,20 @@ mDNSlocal mStatus remove_record(request_state *request) e = *ptr; *ptr = e->next; // unlink - LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, request->hdr.reg_index, RRDisplayString(&mDNSStorage, &e->rr->resrec)); + LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, e->key, RRDisplayString(&mDNSStorage, &e->rr->resrec)); e->rr->RecordContext = NULL; - err = mDNS_Deregister(&mDNSStorage, e->rr); + if (e->external_advertise) + { + external_stop_advertising_service(&e->rr->resrec); + e->external_advertise = mDNSfalse; + } + err = mDNS_Deregister(&mDNSStorage, e->rr); // Will free e->rr for us; we're responsible for freeing e if (err) { LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err); freeL("registered_record_entry AuthRecord remove_record", e->rr); } + freeL("registered_record_entry remove_record", e); return err; } @@ -1957,7 +1247,9 @@ mDNSlocal mStatus remove_extra(const request_state *const request, service_insta if (ptr->ClientID == request->hdr.reg_index) // found match { *rrtype = ptr->r.resrec.rrtype; - return mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr); + if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec); + err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr); + break; } } return err; @@ -2069,8 +1361,19 @@ mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain) { service_instance **ptr, *instance; - int instance_size; + const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0; + const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain); mStatus result; + mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID; + + if (interfaceID == mDNSInterface_P2P) interfaceID = mDNSInterface_Any; + + // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS) + // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast + // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface. + // (Specifying an interface with an apparently wide-area domain (i.e. something other than "local") + // currently forces the registration to use mDNS multicast despite the apparently wide-area domain.) + if (request->u.servicereg.default_domain && !DomainIsLocal) interfaceID = mDNSInterface_Any; for (ptr = &request->u.servicereg.instances; *ptr; ptr = &(*ptr)->next) { @@ -2082,26 +1385,28 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain } } - // Special-case hack: We don't advertise SMB service in AutoTunnel domains, because AutoTunnel - // services have to support IPv6, and our SMB server does not - // <rdar://problem/5482322> BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6 - if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp")) + if (mDNSStorage.KnownBugs & mDNS_KnownBug_LimitedIPv6) { - DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain); - if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported); + // Special-case hack: On Mac OS X 10.6.x and earlier we don't advertise SMB service in AutoTunnel domains, + // because AutoTunnel services have to support IPv6, and in Mac OS X 10.6.x the SMB server does not. + // <rdar://problem/5482322> BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6 + if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp")) + { + DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain); + if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported); + } } - instance_size = sizeof(*instance); - if (request->u.servicereg.txtlen > sizeof(RDataBody)) instance_size += (request->u.servicereg.txtlen - sizeof(RDataBody)); - instance = mallocL("service_instance", instance_size); + instance = mallocL("service_instance", sizeof(*instance) + extra_size); if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } - instance->next = mDNSNULL; - instance->request = request; - instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string); - instance->renameonmemfree = 0; - instance->clientnotified = mDNSfalse; - instance->default_local = (request->u.servicereg.default_domain && SameDomainName(domain, &localdomain)); + instance->next = mDNSNULL; + instance->request = request; + instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string); + instance->renameonmemfree = 0; + instance->clientnotified = mDNSfalse; + instance->default_local = (request->u.servicereg.default_domain && DomainIsLocal); + instance->external_advertise = mDNSfalse; AssignDomainName(&instance->domain, domain); if (request->u.servicereg.num_subtypes && !instance->subtypes) @@ -2113,7 +1418,7 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain request->u.servicereg.port, request->u.servicereg.txtdata, request->u.servicereg.txtlen, instance->subtypes, request->u.servicereg.num_subtypes, - request->u.servicereg.InterfaceID, regservice_callback, instance); + interfaceID, regservice_callback, instance); if (!result) { @@ -2234,7 +1539,6 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc"); mDNSPlatformMemCopy(request->u.servicereg.txtdata, get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen), request->u.servicereg.txtlen); } - else request->u.servicereg.txtdata = NULL; if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } @@ -2398,6 +1702,12 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d b->next = info->u.browser.browsers; info->u.browser.browsers = b; LogOperation("%3d: DNSServiceBrowse(%##s) START", info->sd, b->q.qname.c); + if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain))) + { + domainname tmp; + ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain); + external_start_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR); + } } return err; } @@ -2407,6 +1717,14 @@ mDNSlocal void browse_termination_callback(request_state *info) while (info->u.browser.browsers) { browser_t *ptr = info->u.browser.browsers; + + if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain))) + { + domainname tmp; + ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain); + external_stop_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR); + } + info->u.browser.browsers = ptr->next; LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->sd, ptr->q.qname.c); mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result @@ -2468,13 +1786,20 @@ mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus r // On shutdown, mDNS_Close automatically deregisters all records // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record // from the LocalDomainEnumRecords list, we do this here before we free the memory. + // (This should actually no longer be necessary, now that we do the proper cleanup in + // udsserver_exit. To confirm this, we'll log an error message if we do find a record that + // hasn't been cut from the list yet. If these messages don't appear, we can delete this code.) ARListElem **ptr = &LocalDomainEnumRecords; while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next; - if (*ptr) *ptr = (*ptr)->next; + if (*ptr) { *ptr = (*ptr)->next; LogMsg("FreeARElemCallback: Have to cut %s", ARDisplayString(m, rr)); } mDNSPlatformMemFree(rr->RecordContext); } } +// RegisterLocalOnlyDomainEnumPTR and DeregisterLocalOnlyDomainEnumPTR largely duplicate code in +// "FoundDomain" in uDNS.c for creating and destroying these special mDNSInterface_LocalOnly records. +// We may want to turn the common code into a subroutine. + mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type) { // allocate/register legacy and non-legacy _browse PTR record @@ -2624,8 +1949,9 @@ mDNSexport void udsserver_handle_configchange(mDNS *const m) { ptr->renameonmemfree = 1; if (ptr->clientnotified) SendServiceRemovalNotification(&ptr->srs); - if (mDNS_DeregisterService(m, &ptr->srs)) // If service was deregistered already - regservice_callback(m, &ptr->srs, mStatus_MemFree); // we can re-register immediately + LogInfo("udsserver_handle_configchange: Calling deregister for Service %##s", ptr->srs.RR_PTR.resrec.name->c); + if (mDNS_DeregisterService_drt(m, &ptr->srs, mDNS_Dereg_rapid)) + regservice_callback(m, &ptr->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately } } @@ -2836,6 +2162,7 @@ mDNSlocal void resolve_termination_callback(request_state *request) LogOperation("%3d: DNSServiceResolve(%##s) STOP", request->sd, request->u.resolve.qtxt.qname.c); mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt); mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + if (request->u.resolve.external_advertise) external_stop_resolving_service(&request->u.resolve.qsrv.qname); } mDNSlocal mStatus handle_resolve_request(request_state *request) @@ -2847,7 +2174,13 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) // extract the data from the message DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + mDNSInterfaceID InterfaceID; + mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P); + + + if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny; + + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); if (interfaceIndex && !InterfaceID) { LogMsg("ERROR: handle_resolve_request bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } @@ -2885,11 +2218,14 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qtxt.ExpectUnique = mDNStrue; request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.resolve.qtxt.SuppressUnusable = mDNSfalse; request->u.resolve.qtxt.QuestionCallback = resolve_result_callback; request->u.resolve.qtxt.QuestionContext = request; request->u.resolve.ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); + request->u.resolve.external_advertise = mDNSfalse; + #if 0 if (!AuthorizedDomain(request, &fqdn, AutoBrowseDomains)) return(mStatus_NoError); #endif @@ -2901,7 +2237,13 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) { err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt); if (err) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); - else request->terminate = resolve_termination_callback; + else + { + request->terminate = resolve_termination_callback; + // If the user explicitly passed in P2P, we don't restrict the domain in which we resolve. + if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn))) + { request->u.resolve.external_advertise = mDNStrue; external_start_resolving_service(&fqdn);} + } } return(err); @@ -2929,21 +2271,19 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, (void)m; // Unused #if APPLE_OSX_mDNSResponder - if (question == &req->u.queryrecord.q2) + if (question == &req->u.queryrecord.q2 && question->qtype != req->u.queryrecord.q.qtype && !SameDomainName(&question->qname, &req->u.queryrecord.q.qname)) { mDNS_StopQuery(&mDNSStorage, question); + question->QuestionCallback = mDNSNULL; // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query - if (answer->RecordType == kDNSRecordTypePacketNegative || - (question->qtype == req->u.queryrecord.q.qtype && SameDomainName(&question->qname, &req->u.queryrecord.q.qname))) - question->QuestionCallback = mDNSNULL; - else + if (answer->RecordType != kDNSRecordTypePacketNegative) { *question = req->u.queryrecord.q; question->InterfaceID = mDNSInterface_Unicast; question->ExpectUnique = mDNStrue; + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", req->sd, question->qname.c, DNSTypeName(question->qtype)); mStatus err = mDNS_StartQuery(&mDNSStorage, question); - if (!err) LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", req->sd, question->qname.c, DNSTypeName(question->qtype)); - else LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); + if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err); } return; } @@ -2995,6 +2335,77 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, put_uint32(AddRecord ? answer->rroriginalttl : 0, &data); append_reply(req, rep); +#if APPLE_OSX_mDNSResponder +#if ! NO_WCF + CHECK_WCF_FUNCTION(WCFIsServerRunning) + { + struct xucred x; + socklen_t xucredlen = sizeof(x); + + if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0) + { + if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && + (x.cr_version == XUCRED_VERSION)) + { + struct sockaddr_storage addr; + const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data; + addr.ss_len = 0; + if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA) + { + if (answer->rrtype == kDNSType_A) + { + struct sockaddr_in *sin = (struct sockaddr_in *)&addr; + sin->sin_port = 0; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer)) + LogMsg("queryrecord_result_callback: WCF AF_INET putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in); + addr.ss_family = AF_INET; + } + } + else if (answer->rrtype == kDNSType_AAAA) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; + sin6->sin6_port = 0; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer)) + LogMsg("queryrecord_result_callback: WCF AF_INET6 putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in6); + addr.ss_family = AF_INET6; + } + } + if (addr.ss_len) + { + debugf("queryrecord_result_callback: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len); + CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) + { + WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid); + } + } + } + else if (answer->rrtype == kDNSType_CNAME) + { + domainname cname; + char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; + if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer)) + LogMsg("queryrecord_result_callback: WCF CNAME putRData failed"); + else + { + ConvertDomainNameToCString(&cname, cname_cstr); + CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) + { + WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid); + } + } + } + } + else my_perror("queryrecord_result_callback: ERROR: getsockopt LOCAL_PEERCRED"); + } + } +#endif +#endif } mDNSlocal void queryrecord_termination_callback(request_state *request) @@ -3002,6 +2413,8 @@ mDNSlocal void queryrecord_termination_callback(request_state *request) LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype)); mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check + if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain))) + external_stop_browsing_for_service(&mDNSStorage, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype); if (request->u.queryrecord.q2.QuestionCallback) mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q2); } @@ -3038,13 +2451,19 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) q->ExpectUnique = mDNSfalse; q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable) != 0; q->QuestionCallback = queryrecord_result_callback; q->QuestionContext = request; - LogOperation("%3d: DNSServiceQueryRecord(%##s, %s, %X) START", request->sd, q->qname.c, DNSTypeName(q->qtype), flags); + LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START", request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype)); err = mDNS_StartQuery(&mDNSStorage, q); if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err); - else request->terminate = queryrecord_termination_callback; + else + { + request->terminate = queryrecord_termination_callback; + if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain))) + external_start_browsing_for_service(&mDNSStorage, &q->qname, q->qtype); + } #if APPLE_OSX_mDNSResponder // Workaround for networks using Microsoft Active Directory using "local" as a private internal top-level domain @@ -3068,6 +2487,8 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries // for names in the "local" domain will be safely answered privately before they hit the root name servers. + // Note that in the "my-small-company.local" example above there will typically be an SOA record for + // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case. if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain)) { AssignDomainName(&q2->qname, &localdomain); @@ -3076,9 +2497,9 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) q2->ForceMCast = mDNSfalse; q2->ReturnIntermed = mDNStrue; } + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype)); err = mDNS_StartQuery(&mDNSStorage, q2); - if (!err) LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype)); - else LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err); + if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err); } #endif // APPLE_OSX_mDNSResponder @@ -3247,7 +2668,7 @@ typedef packedstruct mDNSlocal void handle_getproperty_request(request_state *request) { - const mStatus BadParamErr = dnssd_htonl(mStatus_BadParamErr); + const mStatus BadParamErr = dnssd_htonl((mDNSu32)mStatus_BadParamErr); char prop[256]; if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0) { @@ -3332,7 +2753,7 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); - mDNSu8 protocol = get_uint32(&request->msgptr, request->msgend); + mDNSu8 protocol = (mDNSu8)get_uint32(&request->msgptr, request->msgend); (void)flags; // Unused if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr); if (request->msgptr + 8 > request->msgend) request->msgptr = NULL; @@ -3382,6 +2803,8 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) mDNSlocal void addrinfo_termination_callback(request_state *request) { + LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP", request->sd, request->u.addrinfo.q4.qname.c); + if (request->u.addrinfo.q4.QuestionContext) { mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); @@ -3401,7 +2824,7 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) domainname d; mStatus err = 0; - DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo)); @@ -3425,39 +2848,25 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) if (!request->u.addrinfo.protocol) { - NetworkInterfaceInfo *i; - if (IsLocalDomain(&d)) - { - for (i = mDNSStorage.HostInterfaces; i; i = i->next) - { - if ((i->ip.type == mDNSAddrType_IPv4) && !mDNSIPv4AddressIsZero(i->ip.ip.v4)) request->u.addrinfo.protocol |= kDNSServiceProtocol_IPv4; - else if ((i->ip.type == mDNSAddrType_IPv6) && !mDNSIPv6AddressIsZero(i->ip.ip.v6)) request->u.addrinfo.protocol |= kDNSServiceProtocol_IPv6; - } - } - else - { - for (i = mDNSStorage.HostInterfaces; i; i = i->next) - { - if ((i->ip.type == mDNSAddrType_IPv4) && !mDNSv4AddressIsLinkLocal(&i->ip.ip.v4)) request->u.addrinfo.protocol |= kDNSServiceProtocol_IPv4; - else if ((i->ip.type == mDNSAddrType_IPv6) && !mDNSv4AddressIsLinkLocal(&i->ip.ip.v6)) request->u.addrinfo.protocol |= kDNSServiceProtocol_IPv6; - } - } + flags |= kDNSServiceFlagsSuppressUnusable; + request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); } + request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id; + request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr; + request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d; + request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN; + request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse; + request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) { - request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.interface_id; - request->u.addrinfo.q4.Target = zeroAddr; - request->u.addrinfo.q4.qname = d; request->u.addrinfo.q4.qtype = kDNSServiceType_A; - request->u.addrinfo.q4.qclass = kDNSServiceClass_IN; - request->u.addrinfo.q4.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - request->u.addrinfo.q4.ExpectUnique = mDNSfalse; - request->u.addrinfo.q4.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - request->u.addrinfo.q4.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback; request->u.addrinfo.q4.QuestionContext = request; - err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4); if (err != mStatus_NoError) { @@ -3468,29 +2877,25 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)) { - request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id; - request->u.addrinfo.q6.Target = zeroAddr; - request->u.addrinfo.q6.qname = d; request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA; - request->u.addrinfo.q6.qclass = kDNSServiceClass_IN; - request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - request->u.addrinfo.q6.ExpectUnique = mDNSfalse; - request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback; request->u.addrinfo.q6.QuestionContext = request; - err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6); if (err != mStatus_NoError) { LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); request->u.addrinfo.q6.QuestionContext = mDNSNULL; - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) // If we started a query for IPv4, - addrinfo_termination_callback(request); // we need to cancel it + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) + { + // If we started a query for IPv4, we need to cancel it + mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); + request->u.addrinfo.q4.QuestionContext = mDNSNULL; + } } } - LogOperation("%3d: DNSServiceGetAddrInfo(%##s) START", request->sd, d.c); + LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START", + request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c); if (!err) request->terminate = addrinfo_termination_callback; @@ -3523,7 +2928,7 @@ mDNSlocal void read_msg(request_state *req) if (req->ts == t_complete) // this must be death or something is wrong { char buf[4]; // dummy for death notification - int nread = recv(req->sd, buf, 4, 0); + int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data); if (!nread) { req->ts = t_terminated; return; } if (nread < 0) goto rerror; LogMsg("%3d: ERROR: read data from a completed request", req->sd); @@ -3537,7 +2942,7 @@ mDNSlocal void read_msg(request_state *req) if (req->hdr_bytes < sizeof(ipc_msg_hdr)) { mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes; - int nread = recv(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0); + int nread = udsSupportReadFD(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0, req->platform_data); if (nread == 0) { req->ts = t_terminated; return; } if (nread < 0) goto rerror; req->hdr_bytes += nread; @@ -3555,7 +2960,7 @@ mDNSlocal void read_msg(request_state *req) // with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin // for other overhead, this means any message above 70kB is definitely bogus. if (req->hdr.datalen > 70000) - { LogMsg("%3d: ERROR: read_msg - hdr.datalen %lu (%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; } + { LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; } req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES); if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; } req->msgptr = req->msgbuf; @@ -3586,7 +2991,7 @@ mDNSlocal void read_msg(request_state *req) msg.msg_flags = 0; nread = recvmsg(req->sd, &msg, 0); #else - nread = recv(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0); + nread = udsSupportReadFD(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0, req->platform_data); #endif if (nread == 0) { req->ts = t_terminated; return; } if (nread < 0) goto rerror; @@ -3642,7 +3047,7 @@ mDNSlocal void read_msg(request_state *req) dnssd_sockaddr_t cliaddr; #if defined(USE_TCP_LOOPBACK) mDNSOpaque16 port; - int opt = 1; + u_long opt = 1; port.b[0] = req->msgptr[0]; port.b[1] = req->msgptr[1]; req->msgptr += 2; @@ -3684,7 +3089,9 @@ mDNSlocal void read_msg(request_state *req) return; } +#if !defined(USE_TCP_LOOPBACK) got_errfd: +#endif LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]); #if defined(_WIN32) if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0) @@ -3720,9 +3127,6 @@ mDNSlocal void request_callback(int fd, short filter, void *info) { mStatus err = 0; request_state *req = info; -#if defined(_WIN32) - u_long opt = 1; -#endif mDNSs32 min_size = sizeof(DNSServiceFlags); (void)fd; // Unused (void)filter; // Unused @@ -3852,9 +3256,7 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) dnssd_sockaddr_t cliaddr; dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr); dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len); -#if defined(SO_NOSIGPIPE) - int optval = 1; -#elif defined(_WIN32) +#if defined(SO_NOSIGPIPE) || defined(_WIN32) unsigned long optval = 1; #endif @@ -3897,7 +3299,7 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups); #endif // APPLE_OSX_mDNSResponder LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid); - udsSupportAddFDToEventLoop(sd, request_callback, request); + udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data); } } @@ -3930,7 +3332,7 @@ mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt) return mDNSfalse; } - if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL)) + if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL, (void **) NULL)) { my_perror("ERROR: could not add listen socket to event loop"); return mDNSfalse; @@ -3945,9 +3347,6 @@ mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count) dnssd_sockaddr_t laddr; int ret; mDNSu32 i = 0; -#if defined(_WIN32) - u_long opt = 1; -#endif LogInfo("udsserver_init"); @@ -4039,8 +3438,8 @@ mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count) #endif // We start a "LocalOnly" query looking for Automatic Browse Domain records. - // When Domain Enumeration in uDNS.c finds an "lb" record from the network, it creates a - // "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked + // When Domain Enumeration in uDNS.c finds an "lb" record from the network, its "FoundDomain" routine + // creates a "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked mDNS_GetDomains(&mDNSStorage, &mDNSStorage.AutomaticBrowseDomainQ, mDNS_DomainTypeBrowseAutomatic, mDNSNULL, mDNSInterface_LocalOnly, AutomaticBrowseDomainChange, mDNSNULL); @@ -4060,6 +3459,18 @@ error: mDNSexport int udsserver_exit(void) { + // Cancel all outstanding client requests + while (all_requests) AbortUnlinkAndFree(all_requests); + + // Clean up any special mDNSInterface_LocalOnly records we created, both the entries for "local" we + // created in udsserver_init, and others we created as a result of reading local configuration data + while (LocalDomainEnumRecords) + { + ARListElem *rem = LocalDomainEnumRecords; + LocalDomainEnumRecords = LocalDomainEnumRecords->next; + mDNS_Deregister(&mDNSStorage, &rem->ar); + } + // If the launching environment created no listening socket, // that means we created it ourselves, so we should clean it up on exit if (dnssd_SocketValid(listenfd)) @@ -4079,39 +3490,53 @@ mDNSexport int udsserver_exit(void) return 0; } -mDNSlocal void LogClientInfo(mDNS *const m, request_state *req) +mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req) { + char prefix[16]; + if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> "); + else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd); + + usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + if (!req->terminate) - LogMsgNoIdent("%3d: No operation yet on this socket", req->sd); + LogMsgNoIdent("%s No operation yet on this socket", prefix); else if (req->terminate == connection_termination) { - registered_record_entry *p; - LogMsgNoIdent("%3d: DNSServiceCreateConnection", req->sd); + int num_records = 0, num_ops = 0; + const registered_record_entry *p; + const request_state *r; + for (p = req->u.reg_recs; p; p=p->next) num_records++; + for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++; + LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s", prefix, + num_records, num_records != 1 ? "s" : "", + num_ops, num_ops != 1 ? "s" : ""); for (p = req->u.reg_recs; p; p=p->next) LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s", p->key, ARDisplayString(m, p->rr)); + for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r); } else if (req->terminate == regservice_termination_callback) { service_instance *ptr; for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) - LogMsgNoIdent("%3d: DNSServiceRegister %##s %u/%u", - req->sd, ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs)); + LogMsgNoIdent("%s DNSServiceRegister %##s %u/%u", + (ptr == req->u.servicereg.instances) ? prefix : " ", + ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs)); } else if (req->terminate == browse_termination_callback) { browser_t *blist; for (blist = req->u.browser.browsers; blist; blist = blist->next) - LogMsgNoIdent("%3d: DNSServiceBrowse %##s", req->sd, blist->q.qname.c); + LogMsgNoIdent("%s DNSServiceBrowse %##s", (blist == req->u.browser.browsers) ? prefix : " ", blist->q.qname.c); } else if (req->terminate == resolve_termination_callback) - LogMsgNoIdent("%3d: DNSServiceResolve %##s", req->sd, req->u.resolve.qsrv.qname.c); + LogMsgNoIdent("%s DNSServiceResolve %##s", prefix, req->u.resolve.qsrv.qname.c); else if (req->terminate == queryrecord_termination_callback) - LogMsgNoIdent("%3d: DNSServiceQueryRecord %##s (%s)", req->sd, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype)); + LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s)", prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype)); else if (req->terminate == enum_termination_callback) - LogMsgNoIdent("%3d: DNSServiceEnumerateDomains %##s", req->sd, req->u.enumeration.q_all.qname.c); + LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s", prefix, req->u.enumeration.q_all.qname.c); else if (req->terminate == port_mapping_termination_callback) - LogMsgNoIdent("%3d: DNSServiceNATPortMapping %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d", - req->sd, + LogMsgNoIdent("%s DNSServiceNATPortMapping %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d", + prefix, &req->u.pm.NATinfo.ExternalAddress, req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", @@ -4121,35 +3546,35 @@ mDNSlocal void LogClientInfo(mDNS *const m, request_state *req) req->u.pm.NATinfo.NATLease, req->u.pm.NATinfo.Lifetime); else if (req->terminate == addrinfo_termination_callback) - LogMsgNoIdent("%3d: DNSServiceGetAddrInfo %s%s %##s", req->sd, + LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s", prefix, req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", req->u.addrinfo.q4.qname.c); else - LogMsgNoIdent("%3d: Unrecognized operation %p", req->sd, req->terminate); + LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate); } mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy) { - if (!ResourceRecords) LogMsgNoIdent("<None>"); - else + mDNSBool showheader = mDNStrue; + const AuthRecord *ar; + OwnerOptData owner = zeroOwner; + for (ar = ResourceRecords; ar; ar=ar->next) { - const AuthRecord *ar; - mDNSEthAddr owner = zeroEthAddr; - LogMsgNoIdent(" Int Next Expire State"); - for (ar = ResourceRecords; ar; ar=ar->next) + const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID); + if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL)) { - NetworkInterfaceInfo *info = (NetworkInterfaceInfo *)ar->resrec.InterfaceID; - if (ar->WakeUp.HMAC.l[0]) (*proxy)++; - if (!mDNSSameEthAddress(&owner, &ar->WakeUp.HMAC)) + if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire State"); } + if (proxy) (*proxy)++; + if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner))) { - owner = ar->WakeUp.HMAC; - if (ar->WakeUp.password.l[0]) - LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &ar->WakeUp.HMAC, &ar->WakeUp.IMAC, &ar->WakeUp.password, ar->WakeUp.seq); - else if (!mDNSSameEthAddress(&ar->WakeUp.HMAC, &ar->WakeUp.IMAC)) - LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &ar->WakeUp.HMAC, &ar->WakeUp.IMAC, ar->WakeUp.seq); + owner = ar->WakeUp; + if (owner.password.l[0]) + LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq); + else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC)) + LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d", &owner.HMAC, &owner.IMAC, owner.seq); else - LogMsgNoIdent("Proxying for %.6a seq %d", &ar->WakeUp.HMAC, ar->WakeUp.seq); + LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq); } if (AuthRecord_uDNS(ar)) LogMsgNoIdent("%7d %7d %7d %7d %s", @@ -4157,18 +3582,21 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, ar->state, ARDisplayString(m, ar)); - else if (ar->resrec.InterfaceID != mDNSInterface_LocalOnly) + else if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly) + LogMsgNoIdent(" LO %s", ARDisplayString(m, ar)); + else if (ar->resrec.InterfaceID == mDNSInterface_P2P) + LogMsgNoIdent(" PP %s", ARDisplayString(m, ar)); + else LogMsgNoIdent("%7d %7d %7d %7s %s", ar->ThisAPInterval / mDNSPlatformOneSecond, ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, - info ? info->ifname : "ALL", + ifname ? ifname : "ALL", ARDisplayString(m, ar)); - else - LogMsgNoIdent(" LO %s", ARDisplayString(m, ar)); usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); } } + if (showheader) LogMsgNoIdent("<None>"); } mDNSexport void udsserver_info(mDNS *const m) @@ -4180,10 +3608,11 @@ mDNSexport void udsserver_info(mDNS *const m) const CacheRecord *cr; const DNSQuestion *q; const DNameListElem *d; + const SearchListElem *s; LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); - LogMsgNoIdent("------------ Cache -------------"); + LogMsgNoIdent("------------ Cache -------------"); LogMsgNoIdent("Slt Q TTL if U Type rdlen"); for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) @@ -4191,15 +3620,19 @@ mDNSexport void udsserver_info(mDNS *const m) CacheUsed++; // Count one cache entity for the CacheGroup object for (cr = cg->members; cr; cr=cr->next) { - mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; - NetworkInterfaceInfo *info = (NetworkInterfaceInfo *)cr->resrec.InterfaceID; + const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; + const char *ifname; + mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID; + if (!InterfaceID && cr->resrec.rDNSServer) + InterfaceID = cr->resrec.rDNSServer->interface; + ifname = InterfaceNameForID(m, InterfaceID); CacheUsed++; if (cr->CRActiveQuestion) CacheActive++; LogMsgNoIdent("%3d %s%8ld %-7s%s %-6s%s", slot, cr->CRActiveQuestion ? "*" : " ", remain, - info ? info->ifname : "-U-", + ifname ? ifname : "-U-", (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", DNSTypeName(cr->resrec.rrtype), @@ -4215,24 +3648,16 @@ mDNSexport void udsserver_info(mDNS *const m) LogMsgNoIdent("Cache currently contains %lu entities; %lu referenced by active questions", CacheUsed, CacheActive); LogMsgNoIdent("--------- Auth Records ---------"); - LogAuthRecords(m, now, m->ResourceRecords, &ProxyA); + LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL); LogMsgNoIdent("------ Duplicate Records -------"); - LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD); + LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL); - LogMsgNoIdent("----- ServiceRegistrations -----"); - if (!m->ServiceRegistrations) LogMsgNoIdent("<None>"); - else - { - ServiceRecordSet *s; - LogMsgNoIdent(" Int Next Expire State"); - for (s = m->ServiceRegistrations; s; s = s->uDNS_next) - LogMsgNoIdent("%7d %7d %7d %7d %s", - s->RR_SRV.ThisAPInterval / mDNSPlatformOneSecond, - (s->RR_SRV.LastAPTime + s->RR_SRV.ThisAPInterval - now) / mDNSPlatformOneSecond, - s->RR_SRV.expire ? (s->RR_SRV.expire - now) / mDNSPlatformOneSecond : 0, - s->state, ARDisplayString(m, &s->RR_SRV)); - } + LogMsgNoIdent("----- Auth Records Proxied -----"); + LogAuthRecords(m, now, m->ResourceRecords, &ProxyA); + + LogMsgNoIdent("-- Duplicate Records Proxied ---"); + LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD); LogMsgNoIdent("---------- Questions -----------"); if (!m->Questions) LogMsgNoIdent("<None>"); @@ -4240,21 +3665,21 @@ mDNSexport void udsserver_info(mDNS *const m) { CacheUsed = 0; CacheActive = 0; - LogMsgNoIdent(" Int Next if T NumAns Type Name"); + LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name"); for (q = m->Questions; q; q=q->next) { mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond; - mDNSs32 n = (q->LastQTime + q->ThisQInterval - now) / mDNSPlatformOneSecond; - NetworkInterfaceInfo *info = (NetworkInterfaceInfo *)q->InterfaceID; + mDNSs32 n = (NextQSendTime(q) - now) / mDNSPlatformOneSecond; + char *ifname = InterfaceNameForID(m, q->InterfaceID); CacheUsed++; if (q->ThisQInterval) CacheActive++; - LogMsgNoIdent("%6d%6d %-7s%s%s %5d %-6s%##s%s", + LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s", i, n, - info ? info->ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", + ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"), - q->AuthInfo ? "P" : " ", - q->CurrentAnswers, - DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); + PrivateQuery(q) ? "P" : " ", + q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf, + q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); } LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive); @@ -4270,17 +3695,25 @@ mDNSexport void udsserver_info(mDNS *const m) if (!all_requests) LogMsgNoIdent("<None>"); else { - request_state *req; + const request_state *req, *r; for (req = all_requests; req; req=req->next) + { + if (req->primary) // If this is a subbordinate operation, check that the parent is in the list + { + for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent; + LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd); + } + // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info LogClientInfo(m, req); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + foundparent:; + } } LogMsgNoIdent("-------- NAT Traversals --------"); if (!m->NATTraversals) LogMsgNoIdent("<None>"); else { - NATTraversalInfo *nat; + const NATTraversalInfo *nat; for (nat = m->NATTraversals; nat; nat=nat->next) { if (nat->Protocol) @@ -4302,7 +3735,7 @@ mDNSexport void udsserver_info(mDNS *const m) if (!m->AuthInfoList) LogMsgNoIdent("<None>"); else { - DomainAuthInfo *a; + const DomainAuthInfo *a; for (a = m->AuthInfoList; a; a = a->next) LogMsgNoIdent("%##s %##s%s", a->domain.c, a->keyname.c, a->AutoTunnel ? " AutoTunnel" : ""); } @@ -4312,10 +3745,10 @@ mDNSexport void udsserver_info(mDNS *const m) if (!m->TunnelClients) LogMsgNoIdent("<None>"); else { - ClientTunnel *c; + const ClientTunnel *c; for (c = m->TunnelClients; c; c = c->next) - LogMsgNoIdent("%##s local %.16a %.4a remote %.16a %.4a %5d interval %d", - c->dstname.c, &c->loc_inner, &c->loc_outer, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), c->q.ThisQInterval); + LogMsgNoIdent("%##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d", + c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval); } #endif // APPLE_OSX_mDNSResponder @@ -4343,6 +3776,68 @@ mDNSexport void udsserver_info(mDNS *const m) LogMsgNoIdent("--- Auto Registration Domains --"); if (!AutoRegistrationDomains) LogMsgNoIdent("<None>"); else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); + + LogMsgNoIdent("--- Search Domains --"); + if (!SearchList) LogMsgNoIdent("<None>"); + else + { + for (s=SearchList; s; s=s->next) + { + LogMsgNoIdent("%##s", s->domain.c); + } + } + LogMsgNoIdent("---- Task Scheduling Timers ----"); + + if (!m->NewQuestions) + LogMsgNoIdent("NewQuestion <NONE>"); + else + LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)", + m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now, + m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); + + if (!m->NewLocalOnlyQuestions) + LogMsgNoIdent("NewLocalOnlyQuestions <NONE>"); + else + LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)", + m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); + + if (!m->NewLocalRecords) + LogMsgNoIdent("NewLocalRecords <NONE>"); + else + LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords)); + + LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>"); + LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " <NONE>"); + +#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now) + + LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)"); + LogMsgNoIdent("m->timenow %08X %11d", now, now); + LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust); + LogTimer("m->NextScheduledEvent ", m->NextScheduledEvent); + +#ifndef UNICAST_DISABLED + LogTimer("m->NextuDNSEvent ", m->NextuDNSEvent); + LogTimer("m->NextSRVUpdate ", m->NextSRVUpdate); + LogTimer("m->NextScheduledNATOp ", m->NextScheduledNATOp); + LogTimer("m->retryGetAddr ", m->retryGetAddr); +#endif + + LogTimer("m->NextCacheCheck ", m->NextCacheCheck); + LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS); + LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry); + LogTimer("m->DelaySleep ", m->DelaySleep); + + LogTimer("m->NextScheduledQuery ", m->NextScheduledQuery); + LogTimer("m->NextScheduledProbe ", m->NextScheduledProbe); + LogTimer("m->NextScheduledResponse", m->NextScheduledResponse); + + LogTimer("m->SuppressSending ", m->SuppressSending); + LogTimer("m->SuppressProbes ", m->SuppressProbes); + LogTimer("m->ProbeFailTime ", m->ProbeFailTime); + LogTimer("m->DelaySleep ", m->DelaySleep); + LogTimer("m->SleepLimit ", m->SleepLimit); + LogMsgNoIdent("m->RegisterAutoTunnel6 %08X", m->RegisterAutoTunnel6); } #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING @@ -4475,6 +3970,7 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) r->replies = r->replies->next; freeL("reply_state/udsserver_idle", fptr); r->time_blocked = 0; // reset failure counter after successful send + r->unresponsiveness_reports = 0; continue; } else if (result == t_terminated || result == t_error) @@ -4488,15 +3984,24 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) if (r->replies) // If we failed to send everything, check our time_blocked timer { - if (!r->time_blocked) r->time_blocked = NonZeroTime(now); - if (now - r->time_blocked >= 60 * mDNSPlatformOneSecond) + if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; + + if (mDNSStorage.SleepState != SleepState_Awake) r->time_blocked = 0; + else if (!r->time_blocked) r->time_blocked = NonZeroTime(now); + else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1)) { - LogMsg("%3d: Could not write data to client after %ld seconds - aborting connection", r->sd, - (now - r->time_blocked) / mDNSPlatformOneSecond); - LogClientInfo(&mDNSStorage, r); - abort_request(r); + int num = 0; + struct reply_state *x = r->replies; + while (x) { num++; x=x->next; } + LogMsg("%3d: Could not write data to client after %ld seconds, %d repl%s waiting", + r->sd, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies"); + if (++r->unresponsiveness_reports >= 60) + { + LogMsg("%3d: Client unresponsive; aborting connection", r->sd); + LogClientInfo(&mDNSStorage, r); + abort_request(r); + } } - else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; } if (!dnssd_SocketValid(r->sd)) // If this request is finished, unlink it from the list and free the memory @@ -4516,10 +4021,10 @@ struct CompileTimeAssertionChecks_uds_daemon // Check our structures are reasonable sizes. Including overly-large buffers, or embedding // other overly-large structures instead of having a pointer to them, can inadvertently // cause structure sizes (and therefore memory usage) to balloon unreasonably. - char sizecheck_request_state [(sizeof(request_state) <= 1760) ? 1 : -1]; - char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 40) ? 1 : -1]; + char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 1 : -1]; + char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1]; char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1]; - char sizecheck_browser_t [(sizeof(browser_t) <= 992) ? 1 : -1]; + char sizecheck_browser_t [(sizeof(browser_t) <= 1016) ? 1 : -1]; char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1]; char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1]; }; |
