version 258.21
diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c
index c1c5d27..826245b 100644
--- a/Clients/dns-sd.c
+++ b/Clients/dns-sd.c
@@ -1073,7 +1073,7 @@
 		}
 
 	if (argc < 2) goto Fail;        // Minimum command line is the command name and one argument
-	operation = getfirstoption(argc, argv, "EFBZLRPQqCAUNTMISV"
+	operation = getfirstoption(argc, argv, "EFBZLlRPQqCAUNTMISV"
 								#if HAS_NAT_PMP_API
 									"X"
 								#endif
@@ -1117,14 +1117,19 @@
 					err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL);
 					break;
 
-		case 'L':	if (argc < opi+2) goto Fail;
-					typ = (argc < opi+2) ? ""      : argv[opi+1];
-					dom = (argc < opi+3) ? "local" : argv[opi+2];
-					typ = gettype(buffer, typ);
-					if (dom[0] == '.' && dom[1] == 0) dom = "local";   // We allow '.' on the command line as a synonym for "local"
-					printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom);
-					err = DNSServiceResolve(&client, 0, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL);
-					break;
+		case 'l':
+		case 'L':	{
+					DNSServiceFlags rflags = 0;
+					if (argc < opi+2) goto Fail;
+ 					typ = (argc < opi+2) ? ""      : argv[opi+1];
+ 					dom = (argc < opi+3) ? "local" : argv[opi+2];
+ 					typ = gettype(buffer, typ);
+ 					if (dom[0] == '.' && dom[1] == 0) dom = "local";   // We allow '.' on the command line as a synonym for "local"
+ 					printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom);
+					if (operation == 'l') rflags |= kDNSServiceFlagsWakeOnResolve;
+					err = DNSServiceResolve(&client, rflags, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL);
+ 					break;
+					}
 
 		case 'R':	if (argc < opi+4) goto Fail;
 					typ = (argc < opi+2) ? "" : argv[opi+1];
diff --git a/Makefile b/Makefile
index 18b458f..c8c8ef9 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@
 
 include /Developer/Makefiles/pb_makefiles/platform.make
 
-MVERS = "mDNSResponder-258.18"
+MVERS = "mDNSResponder-258.21"
 
 DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
 
diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c
index bb7a082..b0fe39d 100644
--- a/mDNSCore/DNSCommon.c
+++ b/mDNSCore/DNSCommon.c
@@ -1074,6 +1074,7 @@
 	q->ForceMCast          = mDNSfalse;
 	q->ReturnIntermed      = mDNSfalse;
 	q->SuppressUnusable    = mDNSfalse;
+	q->WakeOnResolve       = mDNSfalse;
 	q->QuestionCallback    = callback;
 	q->QuestionContext     = context;
 	}
diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c
index 700282e..51cc367 100755
--- a/mDNSCore/mDNS.c
+++ b/mDNSCore/mDNS.c
@@ -75,6 +75,7 @@
 mDNSlocal void BeginSleepProcessing(mDNS *const m);
 mDNSlocal void RetrySPSRegistrations(mDNS *const m);
 mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password);
+mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q);
 
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
@@ -278,6 +279,9 @@
 #define GoodbyeCount ((mDNSu8)3)
 #define WakeupCount ((mDNSu8)18)
 
+// Number of wakeups we send if WakeOnResolve is set in the question
+#define InitialWakeOnResolveCount ((mDNSu8)3)
+
 // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
 // This means that because the announce interval is doubled after sending the first packet, the first
 // observed on-the-wire inter-packet interval between announcements is actually one second.
@@ -776,6 +780,11 @@
 	rr->InFlightRDLen     = 0;
 	rr->QueuedRData       = 0;
 	rr->QueuedRDLen       = 0;
+	//mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo));
+	// We should be recording the actual internal port for this service record here. Once we initiate our NAT mapping
+	// request we'll subsequently overwrite srv.port with the allocated external NAT port -- potentially multiple
+	// times with different values if the external NAT port changes during the lifetime of the service registration.
+	//if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port;
 
 //	rr->resrec.interface         = already set in mDNS_SetupResourceRecord
 //	rr->resrec.name->c           = MUST be set by client
@@ -2273,6 +2282,57 @@
 	return(i);
 	}
 
+mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q)
+	{
+	int len, i, cnt;
+	mDNSInterfaceID InterfaceID = q->InterfaceID;
+	domainname *d = &q->qname;
+
+	// We can't send magic packets without knowing which interface to send it on.
+	if (InterfaceID == mDNSInterface_Any || InterfaceID == mDNSInterface_LocalOnly || InterfaceID == mDNSInterface_P2P)
+		{
+		LogMsg("mDNSSendWakeOnResolve: ERROR!! Invalid InterfaceID %p for question %##s", InterfaceID, q->qname.c);
+		return;
+		}
+
+	// Split MAC@IPAddress and pass them separately
+	len = d->c[0];
+	i = 1;
+	cnt = 0;
+	for (i = 1; i < len; i++)
+		{
+		if (d->c[i] == '@')
+			{
+			char EthAddr[18];	// ethernet adddress : 12 bytes + 5 ":" + 1 NULL byte
+			char IPAddr[47];    // Max IP address len: 46 bytes (IPv6) + 1 NULL byte
+			if (cnt != 5)
+				{
+				LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, cnt %d", q->qname.c, cnt);
+				return;
+				}
+			if ((i - 1) > (int) (sizeof(EthAddr) - 1))
+				{
+				LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, length %d", q->qname.c, i - 1);
+				return;
+				}
+			if ((len - i) > (int)(sizeof(IPAddr) - 1))
+				{
+				LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed IP address %##s, length %d", q->qname.c, len - i);
+				return;
+				}
+			mDNSPlatformMemCopy(EthAddr, &d->c[1], i - 1);
+			EthAddr[i - 1] = 0;
+			mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i);
+			IPAddr[len - i] = 0;
+			mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount);
+			return;
+			}
+		else if (d->c[i] == ':')
+			cnt++;
+		}
+	LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c);
+	}
+
 mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
 	{
 	// If more than 90% of the way to the query time, we should unconditionally accelerate it
@@ -2568,7 +2628,14 @@
 					// 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);
+						{
+						q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
+						if (q->WakeOnResolveCount)
+							{
+							mDNSSendWakeOnResolve(m, q);
+							q->WakeOnResolveCount--;
+							}
+						}
 					}
 				}
 
@@ -5932,8 +5999,11 @@
 								LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will rename %s", rr->ProbeCount, ARDisplayString(m, rr));
 								mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
 								}
-							// We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the same machine giving
-							// different answers for the reverse mapping record.) This is simply a misconfiguration, and we don't try to recover from it.
+							// We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the
+							// same machine giving different answers for the reverse mapping record, or there are two machines on the
+							// network using the same IP address.) This is simply a misconfiguration, and there's nothing we can do
+							// to fix it -- e.g. it's not our job to be trying to change the machine's IP address. We just discard our
+							// record to avoid continued conflicts (as we do for a conflict on our Unique records) and get on with life.
 							else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
 								{
 								LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr));
@@ -7378,6 +7448,13 @@
 		question->validDNSServers   = zeroOpaque64;
 		question->triedAllServersOnce = 0;
 		question->noServerResponse = 0;
+		if (question->WakeOnResolve)
+			{
+			question->WakeOnResolveCount = InitialWakeOnResolveCount;
+			mDNS_PurgeBeforeResolve(m, question);
+			}
+		else
+			question->WakeOnResolveCount = 0;
 
 		if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo;
 
@@ -7682,6 +7759,7 @@
 	question->ForceMCast       = ForceMCast;
 	question->ReturnIntermed   = mDNSfalse;
 	question->SuppressUnusable = mDNSfalse;
+	question->WakeOnResolve    = mDNSfalse;
 	question->QuestionCallback = Callback;
 	question->QuestionContext  = Context;
 	if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
@@ -7855,6 +7933,7 @@
 	query->qSRV.ForceMCast          = mDNSfalse;
 	query->qSRV.ReturnIntermed      = mDNSfalse;
 	query->qSRV.SuppressUnusable    = mDNSfalse;
+	query->qSRV.WakeOnResolve       = mDNSfalse;
 	query->qSRV.QuestionCallback    = FoundServiceInfoSRV;
 	query->qSRV.QuestionContext     = query;
 
@@ -7869,6 +7948,7 @@
 	query->qTXT.ForceMCast          = mDNSfalse;
 	query->qTXT.ReturnIntermed      = mDNSfalse;
 	query->qTXT.SuppressUnusable    = mDNSfalse;
+	query->qTXT.WakeOnResolve       = mDNSfalse;
 	query->qTXT.QuestionCallback    = FoundServiceInfoTXT;
 	query->qTXT.QuestionContext     = query;
 
@@ -7883,6 +7963,7 @@
 	query->qAv4.ForceMCast          = mDNSfalse;
 	query->qAv4.ReturnIntermed      = mDNSfalse;
 	query->qAv4.SuppressUnusable    = mDNSfalse;
+	query->qAv4.WakeOnResolve       = mDNSfalse;
 	query->qAv4.QuestionCallback    = FoundServiceInfo;
 	query->qAv4.QuestionContext     = query;
 
@@ -7897,6 +7978,7 @@
 	query->qAv6.ForceMCast          = mDNSfalse;
 	query->qAv6.ReturnIntermed      = mDNSfalse;
 	query->qAv6.SuppressUnusable    = mDNSfalse;
+	query->qAv6.WakeOnResolve       = mDNSfalse;
 	query->qAv6.QuestionCallback    = FoundServiceInfo;
 	query->qAv6.QuestionContext     = query;
 
@@ -7947,6 +8029,7 @@
 	question->ForceMCast       = mDNSfalse;
 	question->ReturnIntermed   = mDNSfalse;
 	question->SuppressUnusable = mDNSfalse;
+	question->WakeOnResolve    = mDNSfalse;
 	question->QuestionCallback = Callback;
 	question->QuestionContext  = Context;
 	if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
@@ -9673,6 +9756,22 @@
 		}
 	}
 
+mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q)
+	{
+	const mDNSu32 slot = HashSlot(&q->qname);
+	CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+	CacheRecord *rp;
+
+	for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
+		{
+		if (SameNameRecordAnswersQuestion(&rp->resrec, q))
+			{
+			LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp));
+			mDNS_PurgeCacheResourceRecord(m, rp);
+			}
+		}
+	}
+
 mDNSlocal void CacheRecordResetDNSServer(mDNS *const m, DNSQuestion *q, DNSServer *new)
 	{
 	const mDNSu32 slot = HashSlot(&q->qname);
diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h
index a9881d0..26aa0a8 100755
--- a/mDNSCore/mDNSEmbeddedAPI.h
+++ b/mDNSCore/mDNSEmbeddedAPI.h
@@ -1452,6 +1452,7 @@
 	mDNSs32               LastQTxTime;		// Last time this Q was sent on one (but not necessarily all) interfaces
 	mDNSu32               CNAMEReferrals;	// Count of how many CNAME redirections we've done
 	mDNSBool              SuppressQuery;    // This query should be suppressed and not sent on the wire 
+	mDNSu8                WakeOnResolveCount; // Number of wakes that should be sent on resolve
 
 	// Wide Area fields. These are used internally by the uDNS core
 	UDPSocket            *LocalSocket;
@@ -1493,6 +1494,7 @@
 	mDNSBool              ForceMCast;		// Set by client to force mDNS query, even for apparently uDNS names
 	mDNSBool              ReturnIntermed;	// Set by client to request callbacks for intermediate CNAME/NXDOMAIN results
 	mDNSBool              SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire
+	mDNSBool              WakeOnResolve;    // Send wakeup on resolve
 	mDNSQuestionCallback *QuestionCallback;
 	void                 *QuestionContext;
 	};
@@ -2541,6 +2543,7 @@
 extern void       mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status);
 
 extern void       mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep);
+extern void       mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration);
 
 #ifdef _LEGACY_NAT_TRAVERSAL_
 // Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core.
@@ -2810,15 +2813,15 @@
 	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_DNSQuestion         [(sizeof(DNSQuestion)          <=   762) ? 1 : -1];
+	char sizecheck_ZoneData            [(sizeof(ZoneData)             <=  1598) ? 1 : -1];
 	char sizecheck_NATTraversalInfo    [(sizeof(NATTraversalInfo)     <=   192) ? 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)       <=  7550) ? 1 : -1];
-	char sizecheck_ServiceInfoQuery    [(sizeof(ServiceInfoQuery)     <=  3050) ? 1 : -1];
+	char sizecheck_ServiceInfoQuery    [(sizeof(ServiceInfoQuery)     <=  3090) ? 1 : -1];
 #if APPLE_OSX_mDNSResponder
 	char sizecheck_ClientTunnel        [(sizeof(ClientTunnel)         <=  1104) ? 1 : -1];
 #endif
diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c
index 2d6ba4b..5d0facf 100755
--- a/mDNSCore/uDNS.c
+++ b/mDNSCore/uDNS.c
@@ -553,6 +553,9 @@
 			{
 			LogMsg("Error! Tried to add a NAT traversal that's already in the active list: request %p Prot %d Int %d TTL %d",
 				traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease);
+			#if ForceAlerts
+				*(long*)0 = 0;
+			#endif
 			return(mStatus_AlreadyRegistered);
 			}
 		if (traversal->Protocol && traversal->Protocol == (*n)->Protocol && mDNSSameIPPort(traversal->IntPort, (*n)->IntPort) &&
@@ -1572,6 +1575,7 @@
 	zd->question.ForceMCast          = mDNSfalse;
 	zd->question.ReturnIntermed      = mDNStrue;
 	zd->question.SuppressUnusable    = mDNSfalse;
+	zd->question.WakeOnResolve       = mDNSfalse;
 	zd->question.QuestionCallback    = GetZoneData_QuestionCallback;
 	zd->question.QuestionContext     = zd;
 
@@ -1709,7 +1713,7 @@
 	if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
 		{
 		LogInfo("CompleteRecordNatMap called for %s but no zone information!", ARDisplayString(m, rr));
-		// We need to clear out the NATinfo state so that it will result in re-acuqiring the mapping
+		// We need to clear out the NATinfo state so that it will result in re-acquiring the mapping
 		// and hence this callback called again.
 		if (rr->NATinfo.clientContext)
 			{
@@ -1802,8 +1806,13 @@
 	else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = NATOp_MapUDP;
 	else { LogMsg("StartRecordNatMap: could not determine transport protocol of service %##s", rr->resrec.name->c); return; }
 	
+	//LogMsg("StartRecordNatMap: clientContext %p IntPort %d srv.port %d %s",
+	//	rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr));
 	if (rr->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &rr->NATinfo);
 	rr->NATinfo.Protocol       = protocol;
+
+	// Shouldn't be trying to set IntPort here --
+	// BuildUpdateMessage overwrites srs->RR_SRV.resrec.rdata->u.srv.port with external (mapped) port number
 	rr->NATinfo.IntPort        = rr->resrec.rdata->u.srv.port;
 	rr->NATinfo.RequestedPort  = rr->resrec.rdata->u.srv.port;
 	rr->NATinfo.NATLease       = 0;		// Request default lease
@@ -1818,6 +1827,19 @@
 // record is temporarily left in the ResourceRecords list so that we can initialize later
 // when the target is resolvable. Similarly, when host name changes, we enter regState_NoTarget
 // and we do the same.
+
+// This UnlinkResourceRecord routine is very worrying. It bypasses all the normal cleanup performed
+// by mDNS_Deregister_internal and just unceremoniously cuts the record from the active list.
+// This is why re-regsitering this record was producing syslog messages like this:
+// "Error! Tried to add a NAT traversal that's already in the active list"
+// Right now UnlinkResourceRecord is fortunately only called by RegisterAllServiceRecords,
+// which then immediately calls mDNS_Register_internal to re-register the record, which probably
+// masked more serious problems. Any other use of UnlinkResourceRecord is likely to lead to crashes.
+// For now we'll workaround that specific problem by explicitly calling mDNS_StopNATOperation_internal,
+// but long-term we should either stop cancelling the record registration and then re-registering it,
+// or if we really do need to do this for some reason it should be done via the usual
+// mDNS_Deregister_internal path instead of just cutting the record from the list.
+
 mDNSlocal mStatus UnlinkResourceRecord(mDNS *const m, AuthRecord *const rr)
 	{
 	AuthRecord **list = &m->ResourceRecords;
@@ -1826,6 +1848,15 @@
 		{
 		*list = rr->next;
 		rr->next = mDNSNULL;
+
+		// Temporary workaround to cancel any active NAT mapping operation
+		if (rr->NATinfo.clientContext)
+			{
+			mDNS_StopNATOperation_internal(m, &rr->NATinfo);
+			rr->NATinfo.clientContext = mDNSNULL;
+			if (rr->resrec.rrtype == kDNSType_SRV) rr->resrec.rdata->u.srv.port = rr->NATinfo.IntPort;
+			}
+
 		return(mStatus_NoError);
 		}
 	LogMsg("UnlinkResourceRecord:ERROR!! - no such active record %##s", rr->resrec.name->c);
@@ -2189,6 +2220,7 @@
 	q->ForceMCast       = mDNSfalse;
 	q->ReturnIntermed   = mDNStrue;
 	q->SuppressUnusable = mDNSfalse;
+	q->WakeOnResolve    = mDNSfalse;
 	q->QuestionCallback = FoundStaticHostname;
 	q->QuestionContext  = mDNSNULL;
 
@@ -4786,6 +4818,7 @@
 	question->ForceMCast       = mDNSfalse;
 	question->ReturnIntermed   = mDNSfalse;
 	question->SuppressUnusable = mDNSfalse;
+	question->WakeOnResolve    = mDNSfalse;
 	question->QuestionCallback = FoundCFDomain;
 	question->QuestionContext  = context;
 	LogInfo("mDNS_StartCFQuestion: Start CF domain question %##s", question->qname.c);
@@ -4919,5 +4952,5 @@
 	// other overly-large structures instead of having a pointer to them, can inadvertently
 	// cause structure sizes (and therefore memory usage) to balloon unreasonably.
 	char sizecheck_tcpInfo_t     [(sizeof(tcpInfo_t)      <=  9056) ? 1 : -1];
-	char sizecheck_SearchListElem[(sizeof(SearchListElem) <=  4800) ? 1 : -1];
+	char sizecheck_SearchListElem[(sizeof(SearchListElem) <=  4860) ? 1 : -1];
 	};
diff --git a/mDNSMacOSX/helper-stubs.c b/mDNSMacOSX/helper-stubs.c
index 9e56860..a1bd530 100644
--- a/mDNSMacOSX/helper-stubs.c
+++ b/mDNSMacOSX/helper-stubs.c
@@ -261,3 +261,14 @@
 fin:
 	return err;
 	}
+
+void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration)
+	{
+	kern_return_t kr = KERN_SUCCESS;
+	int retry = 0, err = 0;
+	MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+	kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr, ip_addr, iteration);
+	MACHRETRYLOOP_END(kr, retry, err, fin);
+fin:
+	(void) err;
+	}
diff --git a/mDNSMacOSX/helper.c b/mDNSMacOSX/helper.c
index f4c0771..a3a9046 100644
--- a/mDNSMacOSX/helper.c
+++ b/mDNSMacOSX/helper.c
@@ -47,6 +47,7 @@
 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
 #include <TargetConditionals.h>
 #include <IOKit/pwr_mgt/IOPMLib.h>
+#include <net/bpf.h>
 
 #include "mDNSEmbeddedAPI.h"
 #include "dns_sd.h"
@@ -2349,3 +2350,92 @@
 	update_idle_timer();
 	return KERN_SUCCESS;
 	}
+
+kern_return_t
+do_mDNSSendWakeupPacket(__unused mach_port_t port, unsigned ifid, const char *eth_addr, const char *ip_addr, int iteration, audit_token_t token)
+	{
+	int bpf_fd, i, j;
+	struct ifreq ifr;
+	char ifname[IFNAMSIZ];
+	char packet[512];
+	char *ptr = packet;
+	char bpf_device[12];
+    struct ether_addr *ea;
+	(void) ip_addr; // unused
+	(void) iteration; // unused
+	(void) token; // unused
+
+	if (if_indextoname(ifid, ifname) == NULL)
+		{
+		helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid interface index %u", ifid);
+		return errno;
+		}
+
+    ea = ether_aton(eth_addr);
+	if (ea == NULL)
+		{
+		helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid ethernet address %s", eth_addr);
+		return errno;
+		}
+
+	for (i = 0; i < 100; i++)
+		{
+        snprintf(bpf_device, sizeof(bpf_device), "/dev/bpf%d", i);
+		bpf_fd = open(bpf_device, O_RDWR, 0);
+		if (bpf_fd == -1)
+			continue;
+		else break;
+		}
+
+	if (bpf_fd == -1)
+		{
+		helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket cannot find a bpf device");
+		return ENXIO;
+		}
+
+	memset(&ifr, 0, sizeof(ifr));
+	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+	if (ioctl(bpf_fd, BIOCSETIF, (char *)&ifr) < 0)
+		{
+		helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket BIOCSETIF failed %s", strerror(errno));
+		return errno;
+		}
+
+	// 0x00 Destination address
+    for (i=0; i<6; i++) *ptr++ = ea->octet[i];
+
+    // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us)
+    for (i=0; i<6; i++) *ptr++ = 0;
+
+    // 0x0C Ethertype (0x0842)
+    *ptr++ = 0x08;
+    *ptr++ = 0x42;
+
+    // 0x0E Wakeup sync sequence
+    for (i=0; i<6; i++) *ptr++ = 0xFF;
+
+    // 0x14 Wakeup data
+    for (j=0; j<16; j++) for (i=0; i<6; i++) *ptr++ = ea->octet[i];
+
+    // 0x74 Password
+    for (i=0; i<6; i++) *ptr++ = 0;
+
+	if (write(bpf_fd, packet, ptr - packet) < 0)
+		{
+		helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno));
+		return errno;
+		}
+	helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent unicast eth_addr %s, ip_addr %s", eth_addr, ip_addr);
+	// Send a broadcast one to handle ethernet switches that don't flood forward packets with
+	// unknown mac addresses.
+	for (i=0; i<6; i++) packet[i] = 0xFF;
+	if (write(bpf_fd, packet, ptr - packet) < 0)
+		{
+		helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno));
+		return errno;
+		}
+	helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent broadcast eth_addr %s, ip_addr %s", eth_addr, ip_addr);
+	close(bpf_fd);
+	return KERN_SUCCESS;
+	}
diff --git a/mDNSMacOSX/helper.h b/mDNSMacOSX/helper.h
index 7bf8f49..e9abade 100644
--- a/mDNSMacOSX/helper.h
+++ b/mDNSMacOSX/helper.h
@@ -73,5 +73,6 @@
 extern int  mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
 				v6addr_t local_outer, short local_port, v6addr_t remote_inner,
 				v6addr_t remote_outer, short remote_port, const domainname *const fqdn);
+extern void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration);
 
 #endif /* H_HELPER_H */
diff --git a/mDNSMacOSX/helpermsg.defs b/mDNSMacOSX/helpermsg.defs
index ad946a3..546621e 100644
--- a/mDNSMacOSX/helpermsg.defs
+++ b/mDNSMacOSX/helpermsg.defs
@@ -98,3 +98,11 @@
 								fqdn			: string_t;
 		out						err				: int;
 		ServerAuditToken		token			: audit_token_t);
+
+simpleroutine mDNSSendWakeupPacket(
+								port			: mach_port_t;
+								ifid			: unsigned;
+								eth_addr		: string_t;
+								ip_addr			: string_t;
+								iteration		: int;
+		ServerAuditToken		token			: audit_token_t);
diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c
index 19f5d0a..a3f3ad3 100644
--- a/mDNSMacOSX/mDNSMacOSX.c
+++ b/mDNSMacOSX/mDNSMacOSX.c
@@ -4352,6 +4352,7 @@
 	p->q.ForceMCast       = mDNSfalse;
 	p->q.ReturnIntermed   = mDNStrue;
 	p->q.SuppressUnusable = mDNSfalse;
+	p->q.WakeOnResolve    = mDNSfalse;
 	p->q.QuestionCallback = AutoTunnelCallback;
 	p->q.QuestionContext  = p;
 
@@ -7672,3 +7673,17 @@
 		LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__);
 		}
 	}
+
+mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
+	{
+	mDNSu32 ifindex;
+
+	// Sanity check
+	ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID);
+	if (ifindex <= 0)
+		{
+		LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex);
+		return;
+		}
+	mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration);
+	}
diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c
index df83b8e..6bc9a24 100644
--- a/mDNSPosix/Identify.c
+++ b/mDNSPosix/Identify.c
@@ -220,6 +220,8 @@
 	q->ExpectUnique     = mDNSfalse;	// Don't want to stop after the first response packet
 	q->ForceMCast       = mDNStrue;		// Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
 	q->ReturnIntermed   = mDNStrue;
+	q->SuppressUnusable = mDNSfalse;
+	q->WakeOnResolve    = mDNSfalse;
 	q->QuestionCallback = callback;
 	q->QuestionContext  = NULL;
 
diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
index f205cd0..45aa621 100755
--- a/mDNSPosix/mDNSPosix.c
+++ b/mDNSPosix/mDNSPosix.c
@@ -1359,6 +1359,15 @@
 	return time(NULL);
 	}
 
+mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
+	{
+	(void) m;
+	(void) InterfaceID;
+	(void) EthAddr;
+	(void) IPAddr;
+	(void) iteration;
+	}
+
 mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s)
 	{
 	if (*nfds < s + 1) *nfds = s + 1;
diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h
index 911c70d..d2a9fdd 100644
--- a/mDNSShared/dns_sd.h
+++ b/mDNSShared/dns_sd.h
@@ -77,7 +77,7 @@
  */
 
 #ifndef _DNS_SD_H
-#define _DNS_SD_H 2581800
+#define _DNS_SD_H 2582100
 
 #ifdef  __cplusplus
     extern "C" {
@@ -341,7 +341,7 @@
      * lock or take similar appropriate precautions to serialize those calls.
      */
 
-    kDNSServiceFlagsSuppressUnusable    = 0x8000
+    kDNSServiceFlagsSuppressUnusable    = 0x8000,
 	/*
 	 * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the
 	 * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name)
@@ -350,6 +350,11 @@
 	 * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for
 	 * "hostname".
 	 */
+	kDNSServiceFlagsWakeOnResolve      = 0x40000
+	/*
+	 * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet
+	 * to wake up the client.
+	 */
 
     };
 
diff --git a/mDNSShared/dnssd_clientshim.c b/mDNSShared/dnssd_clientshim.c
index 17f2e93..a891915 100644
--- a/mDNSShared/dnssd_clientshim.c
+++ b/mDNSShared/dnssd_clientshim.c
@@ -523,6 +523,7 @@
 	x->qTXT.ForceMCast          = mDNSfalse;
 	x->qTXT.ReturnIntermed      = mDNSfalse;
 	x->qTXT.SuppressUnusable    = mDNSfalse;
+	x->qTXT.WakeOnResolve       = mDNSfalse;
 	x->qTXT.QuestionCallback    = FoundServiceInfo;
 	x->qTXT.QuestionContext     = x;
 
diff --git a/mDNSShared/dnssd_clientstub.c b/mDNSShared/dnssd_clientstub.c
index 62f640e..fc43730 100644
--- a/mDNSShared/dnssd_clientstub.c
+++ b/mDNSShared/dnssd_clientstub.c
@@ -1094,6 +1094,16 @@
 
 	if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam;
 
+	// Need a real InterfaceID for WakeOnResolve
+	if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 &&
+		((interfaceIndex == kDNSServiceInterfaceIndexAny) ||
+		(interfaceIndex == kDNSServiceInterfaceIndexLocalOnly) ||
+		(interfaceIndex == kDNSServiceInterfaceIndexUnicast) ||
+		(interfaceIndex == kDNSServiceInterfaceIndexP2P)))
+		{
+		return kDNSServiceErr_BadParam;
+		}
+
 	err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, callBack, context);
 	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
 
diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c
index 964667c..9ad7bc5 100644
--- a/mDNSShared/uds_daemon.c
+++ b/mDNSShared/uds_daemon.c
@@ -2203,6 +2203,8 @@
 	request->u.resolve.qsrv.ExpectUnique     = mDNStrue;
 	request->u.resolve.qsrv.ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
 	request->u.resolve.qsrv.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+	request->u.resolve.qsrv.SuppressUnusable = mDNSfalse;
+	request->u.resolve.qsrv.WakeOnResolve    = (flags & kDNSServiceFlagsWakeOnResolve      ) != 0;
 	request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
 	request->u.resolve.qsrv.QuestionContext  = request;
 
@@ -2216,6 +2218,7 @@
 	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.WakeOnResolve    = mDNSfalse;
 	request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
 	request->u.resolve.qtxt.QuestionContext  = request;
 
@@ -2449,6 +2452,7 @@
 	q->ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
 	q->ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
 	q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable) != 0;
+	q->WakeOnResolve    = mDNSfalse;
 	q->QuestionCallback = queryrecord_result_callback;
 	q->QuestionContext  = request;
 
@@ -2858,6 +2862,7 @@
 	request->u.addrinfo.q4.ForceMCast       = request->u.addrinfo.q6.ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
 	request->u.addrinfo.q4.ReturnIntermed   = request->u.addrinfo.q6.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
 	request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
+	request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = mDNSfalse;
 
 	if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
 		{
@@ -4020,7 +4025,7 @@
 	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)               <=  1016) ? 1 : -1];
+	char sizecheck_browser_t              [(sizeof(browser_t)               <=  1026) ? 1 : -1];
 	char sizecheck_reply_hdr              [(sizeof(reply_hdr)               <=   12) ? 1 : -1];
 	char sizecheck_reply_state            [(sizeof(reply_state)             <=   64) ? 1 : -1];
 	};
diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c
index 4add845..58feb7f 100755
--- a/mDNSWindows/mDNSWin32.c
+++ b/mDNSWindows/mDNSWin32.c
@@ -2183,6 +2183,14 @@
 	return err;
 }
 
+mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
+	{
+	(void) m;
+	(void) InterfaceID;
+	(void) EthAddr;
+	(void) IPAddr;
+	(void) iteration;
+	}
 
 #if 0
 #pragma mark -