14b22b93rs/* -*- Mode: C; tab-width: 4 -*-
24b22b93rs *
33b436d0Toomas Soome * Copyright (c) 2002-2018 Apple Inc. All rights reserved.
44b22b93rs *
54b22b93rs * Licensed under the Apache License, Version 2.0 (the "License");
64b22b93rs * you may not use this file except in compliance with the License.
74b22b93rs * You may obtain a copy of the License at
85ffb0c9Toomas Soome *
94b22b93rs *     http://www.apache.org/licenses/LICENSE-2.0
105ffb0c9Toomas Soome *
114b22b93rs * Unless required by applicable law or agreed to in writing, software
124b22b93rs * distributed under the License is distributed on an "AS IS" BASIS,
134b22b93rs * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144b22b93rs * See the License for the specific language governing permissions and
154b22b93rs * limitations under the License.
164b22b93rs *
174b22b93rs * This code is completely 100% portable C. It does not depend on any external header files
184b22b93rs * from outside the mDNS project -- all the types it expects to find are defined right here.
195ffb0c9Toomas Soome *
204b22b93rs * The previous point is very important: This file does not depend on any external
215ffb0c9Toomas Soome * header files. It should compile on *any* platform that has a C compiler, without
224b22b93rs * making *any* assumptions about availability of so-called "standard" C functions,
234b22b93rs * routines, or types (which may or may not be present on any given platform).
245ffb0c9Toomas Soome */
265ffb0c9Toomas Soome#include "DNSCommon.h"                  // Defines general DNS utility routines
275ffb0c9Toomas Soome#include "uDNS.h"                       // Defines entry points into unicast-specific routines
285ffb0c9Toomas Soome#include "nsec.h"
295ffb0c9Toomas Soome#include "dnssec.h"
305ffb0c9Toomas Soome#include "anonymous.h"
325ffb0c9Toomas Soome// Disable certain benign warnings with Microsoft compilers
335ffb0c9Toomas Soome#if (defined(_MSC_VER))
345ffb0c9Toomas Soome// Disable "conditional expression is constant" warning for debug macros.
355ffb0c9Toomas Soome// Otherwise, this generates warnings for the perfectly natural construct "while(1)"
365ffb0c9Toomas Soome// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
375ffb0c9Toomas Soome    #pragma warning(disable:4127)
385ffb0c9Toomas Soome
395ffb0c9Toomas Soome// Disable "assignment within conditional expression".
405ffb0c9Toomas Soome// Other compilers understand the convention that if you place the assignment expression within an extra pair
415ffb0c9Toomas Soome// of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
425ffb0c9Toomas Soome// The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
435ffb0c9Toomas Soome// to the compiler that the assignment is intentional, we have to just turn this warning off completely.
445ffb0c9Toomas Soome    #pragma warning(disable:4706)
455ffb0c9Toomas Soome#endif
475ffb0c9Toomas Soome#include "dns_sd.h" // for kDNSServiceFlags* definitions
48c65ebfcToomas Soome#include "dns_sd_internal.h"
505ffb0c9Toomas Soome#if APPLE_OSX_mDNSResponder
515ffb0c9Toomas Soome#include <WebFilterDNS/WebFilterDNS.h>
53c65ebfcToomas Soome// Delay in seconds before disabling multicast after there are no active queries or registrations.
54c65ebfcToomas Soome#define BONJOUR_DISABLE_DELAY 60
55c65ebfcToomas Soome
565ffb0c9Toomas Soome#if !NO_WCF
575ffb0c9Toomas SoomeWCFConnection *WCFConnectionNew(void) __attribute__((weak_import));
585ffb0c9Toomas Soomevoid WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import));
605ffb0c9Toomas Soome// Do we really need to define a macro for "if"?
615ffb0c9Toomas Soome#define CHECK_WCF_FUNCTION(X) if (X)
625ffb0c9Toomas Soome#endif // ! NO_WCF
645ffb0c9Toomas Soome#else
665ffb0c9Toomas Soome#define NO_WCF 1
675ffb0c9Toomas Soome#endif // APPLE_OSX_mDNSResponder
69c65ebfcToomas Soome#if AWD_METRICS
70cda73f6Toomas Soome#include "Metrics.h"
71cda73f6Toomas Soome#endif
72cda73f6Toomas Soome
73c65ebfcToomas Soome#if USE_DNS64
74c65ebfcToomas Soome#include "DNS64.h"
75c65ebfcToomas Soome#endif
76c65ebfcToomas Soome
77c65ebfcToomas Soome#ifdef UNIT_TEST
78c65ebfcToomas Soome#include "unittest.h"
79c65ebfcToomas Soome#endif
80c65ebfcToomas Soome
815ffb0c9Toomas Soome// Forward declarations
825ffb0c9Toomas SoomemDNSlocal void BeginSleepProcessing(mDNS *const m);
835ffb0c9Toomas SoomemDNSlocal void RetrySPSRegistrations(mDNS *const m);
84c65ebfcToomas SoomemDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password, mDNSBool unicastOnly);
855ffb0c9Toomas SoomemDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
863b436d0Toomas SoomemDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q);
875ffb0c9Toomas SoomemDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q);
885ffb0c9Toomas SoomemDNSlocal void mDNS_SendKeepalives(mDNS *const m);
895ffb0c9Toomas SoomemDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth,
905ffb0c9Toomas Soome                                         mDNSu32 *seq, mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win);
925ffb0c9Toomas SoomemDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m);
935ffb0c9Toomas SoomemDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m);
945ffb0c9Toomas SoomemDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords);
955ffb0c9Toomas SoomemDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end,
965ffb0c9Toomas Soome                                        const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records);
97cda73f6Toomas SoomemDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth);
1004b22b93rs// ***************************************************************************
1024b22b93rs#pragma mark - Program Constants
1055ffb0c9Toomas Soome// To Turn OFF mDNS_Tracer set MDNS_TRACER to 0 or undef it
1065ffb0c9Toomas Soome#define MDNS_TRACER 1
1075ffb0c9Toomas Soome
1085ffb0c9Toomas Soome#define NO_HINFO 1
1104b22b93rs// Any records bigger than this are considered 'large' records
1114b22b93rs#define SmallRecordLimit 1024
1134b22b93rs#define kMaxUpdateCredits 10
1144b22b93rs#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
1165ffb0c9Toomas Soome// define special NR_AnswerTo values
1175ffb0c9Toomas Soome#define NR_AnswerMulticast  (mDNSu8*)~0
1185ffb0c9Toomas Soome#define NR_AnswerUnicast    (mDNSu8*)~1
1195ffb0c9Toomas Soome
120c65ebfcToomas Soome// Question default timeout values
121c65ebfcToomas Soome#define DEFAULT_MCAST_TIMEOUT       5
122c65ebfcToomas Soome#define DEFAULT_LO_OR_P2P_TIMEOUT   5
1235ffb0c9Toomas Soome
1245ffb0c9Toomas Soome// The code (see SendQueries() and BuildQuestion()) needs to have the
1255ffb0c9Toomas Soome// RequestUnicast value set to a value one greater than the number of times you want the query
1265ffb0c9Toomas Soome// sent with the "request unicast response" (QU) bit set.
1275ffb0c9Toomas Soome#define SET_QU_IN_FIRST_QUERY   2
128c65ebfcToomas Soome#define kDefaultRequestUnicastCount SET_QU_IN_FIRST_QUERY
129c65ebfcToomas Soome
130c65ebfcToomas Soome// The time needed to offload records to a sleep proxy after powerd sends the kIOMessageSystemWillSleep notification
131c65ebfcToomas Soome#define DARK_WAKE_DELAY_SLEEP  5
132c65ebfcToomas Soome#define kDarkWakeDelaySleep    (mDNSPlatformOneSecond * DARK_WAKE_DELAY_SLEEP)
133c65ebfcToomas Soome
134c65ebfcToomas Soome// The maximum number of times we delay probing to prevent spurious conflicts due to stale packets
135c65ebfcToomas Soome#define MAX_CONFLICT_PROCESSING_DELAYS 3
136c65ebfcToomas Soome
137c65ebfcToomas Soome// RFC 6762 defines Passive Observation Of Failures (POOF)
138c65ebfcToomas Soome//
139c65ebfcToomas Soome//    A host observes the multicast queries issued by the other hosts on
140c65ebfcToomas Soome//    the network.  One of the major benefits of also sending responses
141c65ebfcToomas Soome//    using multicast is that it allows all hosts to see the responses
142c65ebfcToomas Soome//    (or lack thereof) to those queries.
143c65ebfcToomas Soome//
144c65ebfcToomas Soome//    If a host sees queries, for which a record in its cache would be
145c65ebfcToomas Soome//    expected to be given as an answer in a multicast response, but no
146c65ebfcToomas Soome//    such answer is seen, then the host may take this as an indication
147c65ebfcToomas Soome//    that the record may no longer be valid.
148c65ebfcToomas Soome//
149c65ebfcToomas Soome//    After seeing two or more of these queries, and seeing no multicast
150c65ebfcToomas Soome//    response containing the expected answer within ten seconds, then even
151c65ebfcToomas Soome//    though its TTL may indicate that it is not yet due to expire, that
152c65ebfcToomas Soome//    record SHOULD be flushed from the cache.
153c65ebfcToomas Soome//
154c65ebfcToomas Soome// <https://tools.ietf.org/html/rfc6762#section-10.5>
1555ffb0c9Toomas Soome
156c65ebfcToomas Soome#define POOF_ENABLED 1
1575ffb0c9Toomas Soome
1584b22b93rsmDNSexport const char *const mDNS_DomainTypeNames[] =
1595ffb0c9Toomas Soome{
1605ffb0c9Toomas Soome    "b._dns-sd._udp.",      // Browse
1615ffb0c9Toomas Soome    "db._dns-sd._udp.",     // Default Browse
1625ffb0c9Toomas Soome    "lb._dns-sd._udp.",     // Automatic Browse
1635ffb0c9Toomas Soome    "r._dns-sd._udp.",      // Registration
1645ffb0c9Toomas Soome    "dr._dns-sd._udp."      // Default Registration
1655ffb0c9Toomas Soome};
1674b22b93rs#ifdef UNICAST_DISABLED
1684b22b93rs#define uDNS_IsActiveQuery(q, u) mDNSfalse
1714b22b93rs// ***************************************************************************
1734b22b93rs#pragma mark -
1745ffb0c9Toomas Soome#pragma mark - General Utility Functions
177c65ebfcToomas Soome// Returns true if this is a  unique, authoritative LocalOnly record that answers questions of type
178c65ebfcToomas Soome// A, AAAA , CNAME, or PTR.  The caller should answer the question with this record and not send out
1795ffb0c9Toomas Soome// the question on the wire if LocalOnlyRecordAnswersQuestion() also returns true.
1805ffb0c9Toomas Soome// Main use is to handle /etc/hosts records and the LocalOnly PTR records created for localhost.
1815ffb0c9Toomas Soome#define UniqueLocalOnlyRecord(rr) ((rr)->ARType == AuthRecordLocalOnly && \
1825ffb0c9Toomas Soome                                        (rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \
1835ffb0c9Toomas Soome                                        ((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \
1845ffb0c9Toomas Soome                                         (rr)->resrec.rrtype == kDNSType_CNAME || \
1855ffb0c9Toomas Soome                                         (rr)->resrec.rrtype == kDNSType_PTR))
1865ffb0c9Toomas Soome
1875ffb0c9Toomas SoomemDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q)
1885ffb0c9Toomas Soome{
1895ffb0c9Toomas Soome    mDNS_CheckLock(m);
1905ffb0c9Toomas Soome
1915ffb0c9Toomas Soome    if (m->NextScheduledStopTime - q->StopTime > 0)
1925ffb0c9Toomas Soome        m->NextScheduledStopTime = q->StopTime;
1935ffb0c9Toomas Soome}
1945ffb0c9Toomas Soome
1955ffb0c9Toomas SoomemDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
1965ffb0c9Toomas Soome{
1975ffb0c9Toomas Soome    mDNS_CheckLock(m);
1995ffb0c9Toomas Soome    if (ActiveQuestion(q))
2005ffb0c9Toomas Soome    {
2015ffb0c9Toomas Soome        // Depending on whether this is a multicast or unicast question we want to set either:
2025ffb0c9Toomas Soome        // m->NextScheduledQuery = NextQSendTime(q) or
2035ffb0c9Toomas Soome        // m->NextuDNSEvent      = NextQSendTime(q)
2045ffb0c9Toomas Soome        mDNSs32 *const timer = mDNSOpaque16IsZero(q->TargetQID) ? &m->NextScheduledQuery : &m->NextuDNSEvent;
2055ffb0c9Toomas Soome        if (*timer - NextQSendTime(q) > 0)
2065ffb0c9Toomas Soome            *timer = NextQSendTime(q);
2075ffb0c9Toomas Soome    }
2085ffb0c9Toomas Soome}
2095ffb0c9Toomas Soome
2105ffb0c9Toomas SoomemDNSlocal void ReleaseAuthEntity(AuthHash *r, AuthEntity *e)
2115ffb0c9Toomas Soome{
2125ffb0c9Toomas Soome#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1
2135ffb0c9Toomas Soome    unsigned int i;
2145ffb0c9Toomas Soome    for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF;
2155ffb0c9Toomas Soome#endif
2165ffb0c9Toomas Soome    e->next = r->rrauth_free;
2175ffb0c9Toomas Soome    r->rrauth_free = e;
2185ffb0c9Toomas Soome    r->rrauth_totalused--;
2195ffb0c9Toomas Soome}
2205ffb0c9Toomas Soome
2215ffb0c9Toomas SoomemDNSlocal void ReleaseAuthGroup(AuthHash *r, AuthGroup **cp)
2225ffb0c9Toomas Soome{
2235ffb0c9Toomas Soome    AuthEntity *e = (AuthEntity *)(*cp);
2245ffb0c9Toomas Soome    LogMsg("ReleaseAuthGroup:  Releasing AuthGroup %##s", (*cp)->name->c);
2255ffb0c9Toomas Soome    if ((*cp)->rrauth_tail != &(*cp)->members)
2265ffb0c9Toomas Soome        LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrauth_tail != &(*cp)->members)");
2275ffb0c9Toomas Soome    if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name);
2285ffb0c9Toomas Soome    (*cp)->name = mDNSNULL;
2295ffb0c9Toomas Soome    *cp = (*cp)->next;          // Cut record from list
2305ffb0c9Toomas Soome    ReleaseAuthEntity(r, e);
2315ffb0c9Toomas Soome}
2325ffb0c9Toomas Soome
2335ffb0c9Toomas SoomemDNSlocal AuthEntity *GetAuthEntity(AuthHash *r, const AuthGroup *const PreserveAG)
2345ffb0c9Toomas Soome{
2355ffb0c9Toomas Soome    AuthEntity *e = mDNSNULL;
2365ffb0c9Toomas Soome
2375ffb0c9Toomas Soome    if (r->rrauth_lock) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
2385ffb0c9Toomas Soome    r->rrauth_lock = 1;
2395ffb0c9Toomas Soome
2405ffb0c9Toomas Soome    if (!r->rrauth_free)
2415ffb0c9Toomas Soome    {
2425ffb0c9Toomas Soome        // We allocate just one AuthEntity at a time because we need to be able
2435ffb0c9Toomas Soome        // free them all individually which normally happens when we parse /etc/hosts into
2445ffb0c9Toomas Soome        // AuthHash where we add the "new" entries and discard (free) the already added
2455ffb0c9Toomas Soome        // entries. If we allocate as chunks, we can't free them individually.
2465ffb0c9Toomas Soome        AuthEntity *storage = mDNSPlatformMemAllocate(sizeof(AuthEntity));
2475ffb0c9Toomas Soome        storage->next = mDNSNULL;
2485ffb0c9Toomas Soome        r->rrauth_free = storage;
2495ffb0c9Toomas Soome    }
2505ffb0c9Toomas Soome
2515ffb0c9Toomas Soome    // If we still have no free records, recycle all the records we can.
2525ffb0c9Toomas Soome    // Enumerating the entire auth is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
2535ffb0c9Toomas Soome    if (!r->rrauth_free)
2545ffb0c9Toomas Soome    {
2555ffb0c9Toomas Soome        mDNSu32 oldtotalused = r->rrauth_totalused;
2565ffb0c9Toomas Soome        mDNSu32 slot;
2575ffb0c9Toomas Soome        for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
2585ffb0c9Toomas Soome        {
2595ffb0c9Toomas Soome            AuthGroup **cp = &r->rrauth_hash[slot];
2605ffb0c9Toomas Soome            while (*cp)
2615ffb0c9Toomas Soome            {
2625ffb0c9Toomas Soome                if ((*cp)->members || (*cp)==PreserveAG) cp=&(*cp)->next;
2635ffb0c9Toomas Soome                else ReleaseAuthGroup(r, cp);
2645ffb0c9Toomas Soome            }
2655ffb0c9Toomas Soome        }
2665ffb0c9Toomas Soome        LogInfo("GetAuthEntity: Recycled %d records to reduce auth cache from %d to %d",
2675ffb0c9Toomas Soome                oldtotalused - r->rrauth_totalused, oldtotalused, r->rrauth_totalused);
2685ffb0c9Toomas Soome    }
2695ffb0c9Toomas Soome
2705ffb0c9Toomas Soome    if (r->rrauth_free) // If there are records in the free list, take one
2715ffb0c9Toomas Soome    {
2725ffb0c9Toomas Soome        e = r->rrauth_free;
2735ffb0c9Toomas Soome        r->rrauth_free = e->next;
2745ffb0c9Toomas Soome        if (++r->rrauth_totalused >= r->rrauth_report)
2755ffb0c9Toomas Soome        {
2765ffb0c9Toomas Soome            LogInfo("RR Auth now using %ld objects", r->rrauth_totalused);
2775ffb0c9Toomas Soome            if      (r->rrauth_report <  100) r->rrauth_report += 10;
2785ffb0c9Toomas Soome            else if (r->rrauth_report < 1000) r->rrauth_report += 100;
2795ffb0c9Toomas Soome            else r->rrauth_report += 1000;
2805ffb0c9Toomas Soome        }
2815ffb0c9Toomas Soome        mDNSPlatformMemZero(e, sizeof(*e));
2825ffb0c9Toomas Soome    }
2835ffb0c9Toomas Soome
2845ffb0c9Toomas Soome    r->rrauth_lock = 0;
2855ffb0c9Toomas Soome
2865ffb0c9Toomas Soome    return(e);
2875ffb0c9Toomas Soome}
2885ffb0c9Toomas Soome
289c65ebfcToomas SoomemDNSexport AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 namehash, const domainname *const name)
2905ffb0c9Toomas Soome{
2915ffb0c9Toomas Soome    AuthGroup *ag;
292c65ebfcToomas Soome    const mDNSu32 slot = namehash % AUTH_HASH_SLOTS;
293c65ebfcToomas Soome
2945ffb0c9Toomas Soome    for (ag = r->rrauth_hash[slot]; ag; ag=ag->next)
2955ffb0c9Toomas Soome        if (ag->namehash == namehash && SameDomainName(ag->name, name))
2965ffb0c9Toomas Soome            break;
2975ffb0c9Toomas Soome    return(ag);
2985ffb0c9Toomas Soome}
2995ffb0c9Toomas Soome
300c65ebfcToomas SoomemDNSexport AuthGroup *AuthGroupForRecord(AuthHash *r, const ResourceRecord *const rr)
3015ffb0c9Toomas Soome{
302c65ebfcToomas Soome    return(AuthGroupForName(r, rr->namehash, rr->name));
3035ffb0c9Toomas Soome}
3045ffb0c9Toomas Soome
305c65ebfcToomas SoomemDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const ResourceRecord *const rr)
3065ffb0c9Toomas Soome{
3075ffb0c9Toomas Soome    mDNSu16 namelen = DomainNameLength(rr->name);
3085ffb0c9Toomas Soome    AuthGroup *ag = (AuthGroup*)GetAuthEntity(r, mDNSNULL);
309c65ebfcToomas Soome    const mDNSu32 slot = rr->namehash % AUTH_HASH_SLOTS;
3105ffb0c9Toomas Soome    if (!ag) { LogMsg("GetAuthGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); }
3115ffb0c9Toomas Soome    ag->next         = r->rrauth_hash[slot];
3125ffb0c9Toomas Soome    ag->namehash     = rr->namehash;
3135ffb0c9Toomas Soome    ag->members      = mDNSNULL;
3145ffb0c9Toomas Soome    ag->rrauth_tail  = &ag->members;
3155ffb0c9Toomas Soome    ag->NewLocalOnlyRecords = mDNSNULL;
3165ffb0c9Toomas Soome    if (namelen > sizeof(ag->namestorage))
3175ffb0c9Toomas Soome        ag->name = mDNSPlatformMemAllocate(namelen);
3185ffb0c9Toomas Soome    else
3195ffb0c9Toomas Soome        ag->name = (domainname*)ag->namestorage;
3205ffb0c9Toomas Soome    if (!ag->name)
3215ffb0c9Toomas Soome    {
3225ffb0c9Toomas Soome        LogMsg("GetAuthGroup: Failed to allocate name storage for %##s", rr->name->c);
3235ffb0c9Toomas Soome        ReleaseAuthEntity(r, (AuthEntity*)ag);
3245ffb0c9Toomas Soome        return(mDNSNULL);
3255ffb0c9Toomas Soome    }
3265ffb0c9Toomas Soome    AssignDomainName(ag->name, rr->name);
3275ffb0c9Toomas Soome
328c65ebfcToomas Soome    if (AuthGroupForRecord(r, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c);
3295ffb0c9Toomas Soome    r->rrauth_hash[slot] = ag;
330c65ebfcToomas Soome    if (AuthGroupForRecord(r, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c);
3315ffb0c9Toomas Soome
3325ffb0c9Toomas Soome    return(ag);
3335ffb0c9Toomas Soome}
3345ffb0c9Toomas Soome
3355ffb0c9Toomas Soome// Returns the AuthGroup in which the AuthRecord was inserted
3365ffb0c9Toomas SoomemDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
3375ffb0c9Toomas Soome{
3385ffb0c9Toomas Soome    AuthGroup *ag;
339c65ebfcToomas Soome
340c65ebfcToomas Soome    (void)m;
341c65ebfcToomas Soome    ag = AuthGroupForRecord(r, &rr->resrec);
342c65ebfcToomas Soome    if (!ag) ag = GetAuthGroup(r, &rr->resrec);   // If we don't have a AuthGroup for this name, make one now
3435ffb0c9Toomas Soome    if (ag)
3445ffb0c9Toomas Soome    {
3455ffb0c9Toomas Soome        *(ag->rrauth_tail) = rr;                // Append this record to tail of cache slot list
3465ffb0c9Toomas Soome        ag->rrauth_tail = &(rr->next);          // Advance tail pointer
3475ffb0c9Toomas Soome    }
3485ffb0c9Toomas Soome    return ag;
3495ffb0c9Toomas Soome}
3505ffb0c9Toomas Soome
3515ffb0c9Toomas SoomemDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
3525ffb0c9Toomas Soome{
3535ffb0c9Toomas Soome    AuthGroup *a;
3545ffb0c9Toomas Soome    AuthRecord **rp;
3555ffb0c9Toomas Soome
356c65ebfcToomas Soome    a = AuthGroupForRecord(r, &rr->resrec);
3575ffb0c9Toomas Soome    if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; }
358c65ebfcToomas Soome    rp = &a->members;
3595ffb0c9Toomas Soome    while (*rp)
3605ffb0c9Toomas Soome    {
3615ffb0c9Toomas Soome        if (*rp != rr)
3625ffb0c9Toomas Soome            rp=&(*rp)->next;
3635ffb0c9Toomas Soome        else
3645ffb0c9Toomas Soome        {
3655ffb0c9Toomas Soome            // We don't break here, so that we can set the tail below without tracking "prev" pointers
3665ffb0c9Toomas Soome
3675ffb0c9Toomas Soome            LogInfo("RemoveAuthRecord: removing auth record %s from table", ARDisplayString(m, rr));
3685ffb0c9Toomas Soome            *rp = (*rp)->next;          // Cut record from list
3695ffb0c9Toomas Soome        }
3705ffb0c9Toomas Soome    }
3715ffb0c9Toomas Soome    // TBD: If there are no more members, release authgroup ?
372c65ebfcToomas Soome    a->rrauth_tail = rp;
3735ffb0c9Toomas Soome    return a;
3745ffb0c9Toomas Soome}
3755ffb0c9Toomas Soome
376c65ebfcToomas SoomemDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 namehash, const domainname *const name)
3775ffb0c9Toomas Soome{
3785ffb0c9Toomas Soome    CacheGroup *cg;
379c65ebfcToomas Soome    mDNSu32    slot = HashSlotFromNameHash(namehash);
3805ffb0c9Toomas Soome    for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
3815ffb0c9Toomas Soome        if (cg->namehash == namehash && SameDomainName(cg->name, name))
3825ffb0c9Toomas Soome            break;
3835ffb0c9Toomas Soome    return(cg);
3845ffb0c9Toomas Soome}
386c65ebfcToomas SoomemDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const ResourceRecord *const rr)
3875ffb0c9Toomas Soome{
388c65ebfcToomas Soome    return(CacheGroupForName(m, rr->namehash, rr->name));
3895ffb0c9Toomas Soome}
3905ffb0c9Toomas Soome
391cda73f6Toomas SoomemDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
3925ffb0c9Toomas Soome{
3935ffb0c9Toomas Soome    NetworkInterfaceInfo *intf;
3945ffb0c9Toomas Soome
3955ffb0c9Toomas Soome    if (addr->type == mDNSAddrType_IPv4)
3965ffb0c9Toomas Soome    {
3975ffb0c9Toomas Soome        // Normally we resist touching the NotAnInteger fields, but here we're doing tricky bitwise masking so we make an exception
3985ffb0c9Toomas Soome        if (mDNSv4AddressIsLinkLocal(&addr->ip.v4)) return(mDNStrue);
3995ffb0c9Toomas Soome        for (intf = m->HostInterfaces; intf; intf = intf->next)
4005ffb0c9Toomas Soome            if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
4015ffb0c9Toomas Soome                if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0)
4025ffb0c9Toomas Soome                    return(mDNStrue);
4035ffb0c9Toomas Soome    }
4045ffb0c9Toomas Soome
4055ffb0c9Toomas Soome    if (addr->type == mDNSAddrType_IPv6)
4065ffb0c9Toomas Soome    {
407cda73f6Toomas Soome        if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue);
4085ffb0c9Toomas Soome        for (intf = m->HostInterfaces; intf; intf = intf->next)
4095ffb0c9Toomas Soome            if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
4105ffb0c9Toomas Soome                if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) &&
4115ffb0c9Toomas Soome                    (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) &&
4125ffb0c9Toomas Soome                    (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) &&
4135ffb0c9Toomas Soome                    (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0))
4145ffb0c9Toomas Soome                        return(mDNStrue);
4155ffb0c9Toomas Soome    }
4165ffb0c9Toomas Soome
4175ffb0c9Toomas Soome    return(mDNSfalse);
4185ffb0c9Toomas Soome}
4195ffb0c9Toomas Soome
4205ffb0c9Toomas SoomemDNSlocal NetworkInterfaceInfo *FirstInterfaceForID(mDNS *const m, const mDNSInterfaceID InterfaceID)
4215ffb0c9Toomas Soome{
4225ffb0c9Toomas Soome    NetworkInterfaceInfo *intf = m->HostInterfaces;
4235ffb0c9Toomas Soome    while (intf && intf->InterfaceID != InterfaceID) intf = intf->next;
4245ffb0c9Toomas Soome    return(intf);
4255ffb0c9Toomas Soome}
4265ffb0c9Toomas Soome
4275ffb0c9Toomas SoomemDNSlocal NetworkInterfaceInfo *FirstIPv4LLInterfaceForID(mDNS *const m, const mDNSInterfaceID InterfaceID)
4285ffb0c9Toomas Soome{
4295ffb0c9Toomas Soome    NetworkInterfaceInfo *intf;
4305ffb0c9Toomas Soome
4315ffb0c9Toomas Soome    if (!InterfaceID)
4325ffb0c9Toomas Soome        return mDNSNULL;
4335ffb0c9Toomas Soome
434c65ebfcToomas Soome    // Note: We don't check for InterfaceActive, as the active interface could be IPv6 and
4355ffb0c9Toomas Soome    // we still want to find the first IPv4 Link-Local interface
4365ffb0c9Toomas Soome    for (intf = m->HostInterfaces; intf; intf = intf->next)
4375ffb0c9Toomas Soome    {
4385ffb0c9Toomas Soome        if (intf->InterfaceID == InterfaceID &&
4395ffb0c9Toomas Soome            intf->ip.type == mDNSAddrType_IPv4 && mDNSv4AddressIsLinkLocal(&intf->ip.ip.v4))
4405ffb0c9Toomas Soome        {
4415ffb0c9Toomas Soome            debugf("FirstIPv4LLInterfaceForID: found LL interface with address %.4a", &intf->ip.ip.v4);
4425ffb0c9Toomas Soome            return intf;
4435ffb0c9Toomas Soome        }
4445ffb0c9Toomas Soome    }
4455ffb0c9Toomas Soome    return (mDNSNULL);
4465ffb0c9Toomas Soome}
4475ffb0c9Toomas Soome
4485ffb0c9Toomas SoomemDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID)
4495ffb0c9Toomas Soome{
4505ffb0c9Toomas Soome    NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
4515ffb0c9Toomas Soome    return(intf ? intf->ifname : mDNSNULL);
4525ffb0c9Toomas Soome}
4535ffb0c9Toomas Soome
4545ffb0c9Toomas Soome// Caller should hold the lock
455c65ebfcToomas SoomemDNSlocal void GenerateNegativeResponse(mDNS *const m, mDNSInterfaceID InterfaceID, QC_result qc)
4565ffb0c9Toomas Soome{
4575ffb0c9Toomas Soome    DNSQuestion *q;
4585ffb0c9Toomas Soome    if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; }
4595ffb0c9Toomas Soome    q = m->CurrentQuestion;
4605ffb0c9Toomas Soome    LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4615ffb0c9Toomas Soome
462c65ebfcToomas Soome    MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, InterfaceID, mDNSNULL);
463c65ebfcToomas Soome
4645ffb0c9Toomas Soome    // We need to force the response through in the following cases
4655ffb0c9Toomas Soome    //
4665ffb0c9Toomas Soome    //  a) SuppressUnusable questions that are suppressed
4675ffb0c9Toomas Soome    //  b) Append search domains and retry the question
4685ffb0c9Toomas Soome    //
4695ffb0c9Toomas Soome    // The question may not have set Intermediates in which case we don't deliver negative responses. So, to force
4705ffb0c9Toomas Soome    // through we use "QC_forceresponse".
4715ffb0c9Toomas Soome    AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, qc);
4725ffb0c9Toomas Soome    if (m->CurrentQuestion == q) { q->ThisQInterval = 0; }              // Deactivate this question
4735ffb0c9Toomas Soome    // Don't touch the question after this
4745ffb0c9Toomas Soome    m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
4755ffb0c9Toomas Soome}
4765ffb0c9Toomas Soome
4775ffb0c9Toomas SoomemDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr)
4785ffb0c9Toomas Soome{
4795ffb0c9Toomas Soome    const mDNSBool selfref = SameDomainName(&q->qname, &rr->rdata->u.name);
4805ffb0c9Toomas Soome    if (q->CNAMEReferrals >= 10 || selfref)
4815ffb0c9Toomas Soome    {
4825ffb0c9Toomas Soome        LogMsg("AnswerQuestionByFollowingCNAME: %p %##s (%s) NOT following CNAME referral %d%s for %s",
4835ffb0c9Toomas Soome               q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr));
4845ffb0c9Toomas Soome    }
4855ffb0c9Toomas Soome    else
4865ffb0c9Toomas Soome    {
4875ffb0c9Toomas Soome        const mDNSu32 c = q->CNAMEReferrals + 1;        // Stash a copy of the new q->CNAMEReferrals value
4885ffb0c9Toomas Soome        UDPSocket *sock = q->LocalSocket;
4895ffb0c9Toomas Soome        mDNSOpaque16 id = q->TargetQID;
490c65ebfcToomas Soome#if AWD_METRICS
491c65ebfcToomas Soome        uDNSMetrics metrics;
492cda73f6Toomas Soome#endif
4935ffb0c9Toomas Soome
494c65ebfcToomas Soome        q->LocalSocket = mDNSNULL;
4955ffb0c9Toomas Soome
4965ffb0c9Toomas Soome        // The SameDomainName check above is to ignore bogus CNAME records that point right back at
4975ffb0c9Toomas Soome        // themselves. Without that check we can get into a case where we have two duplicate questions,
4985ffb0c9Toomas Soome        // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals
4995ffb0c9Toomas Soome        // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because
5005ffb0c9Toomas Soome        // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates
5015ffb0c9Toomas Soome        // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals
5025ffb0c9Toomas Soome        // for either of them. This is not a problem for CNAME loops of two or more records because in
5035ffb0c9Toomas Soome        // those cases the newly re-appended question A has a different target name and therefore cannot be
5045ffb0c9Toomas Soome        // a duplicate of any other question ('B') which was itself a duplicate of the previous question A.
5055ffb0c9Toomas Soome
5065ffb0c9Toomas Soome        // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect,
5075ffb0c9Toomas Soome        // and track CNAMEs coming and going, we should really create a subordinate query here,
5085ffb0c9Toomas Soome        // which we would subsequently cancel and retract if the CNAME referral record were removed.
5095ffb0c9Toomas Soome        // In reality this is such a corner case we'll ignore it until someone actually needs it.
5105ffb0c9Toomas Soome
5115ffb0c9Toomas Soome        LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s",
5125ffb0c9Toomas Soome                q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr));
5135ffb0c9Toomas Soome
514c65ebfcToomas Soome#if AWD_METRICS
515c65ebfcToomas Soome        if ((q->CNAMEReferrals == 0) && !q->metrics.originalQName)
516cda73f6Toomas Soome        {
517c65ebfcToomas Soome            domainname *    qName;
518c65ebfcToomas Soome            mDNSu16         qNameLen;
519cda73f6Toomas Soome
520cda73f6Toomas Soome            qNameLen = DomainNameLength(&q->qname);
521cda73f6Toomas Soome            if ((qNameLen > 0) && (qNameLen <= MAX_DOMAIN_NAME))
522cda73f6Toomas Soome            {
523c65ebfcToomas Soome                qName = mDNSPlatformMemAllocate(qNameLen);
524c65ebfcToomas Soome                if (qName)
525cda73f6Toomas Soome                {
526c65ebfcToomas Soome                    mDNSPlatformMemCopy(qName->c, q->qname.c, qNameLen);
527c65ebfcToomas Soome                    q->metrics.originalQName = qName;
528cda73f6Toomas Soome                }
529cda73f6Toomas Soome            }
530cda73f6Toomas Soome        }
531c65ebfcToomas Soome        metrics = q->metrics;
532c65ebfcToomas Soome        mDNSPlatformMemZero(&q->metrics, sizeof(q->metrics));
533cda73f6Toomas Soome#endif
5345ffb0c9Toomas Soome        mDNS_StopQuery_internal(m, q);                              // Stop old query
5355ffb0c9Toomas Soome        AssignDomainName(&q->qname, &rr->rdata->u.name);            // Update qname
5365ffb0c9Toomas Soome        q->qnamehash = DomainNameHashValue(&q->qname);              // and namehash
5375ffb0c9Toomas Soome        // If a unicast query results in a CNAME that points to a .local, we need to re-try
5385ffb0c9Toomas Soome        // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal
5395ffb0c9Toomas Soome        // to try this as unicast query even though it is a .local name
5405ffb0c9Toomas Soome        if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname))
5415ffb0c9Toomas Soome        {
5425ffb0c9Toomas Soome            LogInfo("AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p %##s (%s) Record %s",
5435ffb0c9Toomas Soome                    q, q->qname.c, DNSTypeName(q->qtype), RRDisplayString(m, rr));
5445ffb0c9Toomas Soome            q->InterfaceID = mDNSInterface_Unicast;
5455ffb0c9Toomas Soome        }
5465ffb0c9Toomas Soome        mDNS_StartQuery_internal(m, q);                             // start new query
5475ffb0c9Toomas Soome        // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal,
5485ffb0c9Toomas Soome        // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero
5495ffb0c9Toomas Soome        q->CNAMEReferrals = c;
550c65ebfcToomas Soome#if AWD_METRICS
5513b436d0Toomas Soome        metrics.expiredAnswerState = q->metrics.expiredAnswerState; //  We want the newly initialized state for this value
552c65ebfcToomas Soome        q->metrics = metrics;
553cda73f6Toomas Soome#endif
5545ffb0c9Toomas Soome        if (sock)
5555ffb0c9Toomas Soome        {
556c65ebfcToomas Soome            // If our new query is a duplicate, then it can't have a socket of its own, so we have to close the one we saved.
557c65ebfcToomas Soome            if (q->DuplicateOf) mDNSPlatformUDPClose(sock);
558c65ebfcToomas Soome            else
559c65ebfcToomas Soome            {
560c65ebfcToomas Soome                // Transplant the old socket into the new question, and copy the query ID across too.
561c65ebfcToomas Soome                // No need to close the old q->LocalSocket value because it won't have been created yet (they're made lazily on-demand).
562c65ebfcToomas Soome                q->LocalSocket = sock;
563c65ebfcToomas Soome                q->TargetQID = id;
564c65ebfcToomas Soome            }
5655ffb0c9Toomas Soome        }
5665ffb0c9Toomas Soome    }
5675ffb0c9Toomas Soome}
5685ffb0c9Toomas Soome
569c65ebfcToomas Soome#ifdef USE_LIBIDN
570c65ebfcToomas Soome
571c65ebfcToomas Soome#include <unicode/uidna.h>
572c65ebfcToomas Soome
573c65ebfcToomas Soome// #define DEBUG_PUNYCODE 1
574c65ebfcToomas Soome
575c65ebfcToomas SoomemDNSlocal mDNSu8 *PunycodeConvert(const mDNSu8 *const src, mDNSu8 *const dst, const mDNSu8 *const end)
576c65ebfcToomas Soome{
577c65ebfcToomas Soome    UErrorCode errorCode = U_ZERO_ERROR;
578c65ebfcToomas Soome    UIDNAInfo info = UIDNA_INFO_INITIALIZER;
579c65ebfcToomas Soome    UIDNA *uts46 = uidna_openUTS46(UIDNA_USE_STD3_RULES|UIDNA_NONTRANSITIONAL_TO_UNICODE, &errorCode);
580c65ebfcToomas Soome    int32_t len = uidna_nameToASCII_UTF8(uts46, (const char *)src+1, src[0], (char *)dst+1, end-(dst+1), &info, &errorCode);
581c65ebfcToomas Soome    uidna_close(uts46);
582c65ebfcToomas Soome    #if DEBUG_PUNYCODE
583c65ebfcToomas Soome    if (errorCode) LogMsg("uidna_nameToASCII_UTF8(%##s) failed errorCode %d", src, errorCode);
584c65ebfcToomas Soome    if (info.errors) LogMsg("uidna_nameToASCII_UTF8(%##s) failed info.errors 0x%08X", src, info.errors);
585c65ebfcToomas Soome    if (len > MAX_DOMAIN_LABEL) LogMsg("uidna_nameToASCII_UTF8(%##s) result too long %d", src, len);
586c65ebfcToomas Soome    #endif
587c65ebfcToomas Soome    if (errorCode || info.errors || len > MAX_DOMAIN_LABEL) return mDNSNULL;
588c65ebfcToomas Soome    *dst = len;
589c65ebfcToomas Soome    return(dst + 1 + len);
590c65ebfcToomas Soome}
591c65ebfcToomas Soome
592c65ebfcToomas SoomemDNSlocal mDNSBool IsHighASCIILabel(const mDNSu8 *d)
593c65ebfcToomas Soome{
594c65ebfcToomas Soome    int i;
595c65ebfcToomas Soome    for (i=1; i<=d[0]; i++) if (d[i] & 0x80) return mDNStrue;
596c65ebfcToomas Soome    return mDNSfalse;
597c65ebfcToomas Soome}
598c65ebfcToomas Soome
599c65ebfcToomas SoomemDNSlocal const mDNSu8 *FindLastHighASCIILabel(const domainname *const d)
600c65ebfcToomas Soome{
601c65ebfcToomas Soome    const mDNSu8 *ptr = d->c;
602c65ebfcToomas Soome    const mDNSu8 *ans = mDNSNULL;
603c65ebfcToomas Soome    while (ptr[0])
604c65ebfcToomas Soome    {
605c65ebfcToomas Soome        const mDNSu8 *const next = ptr + 1 + ptr[0];
606c65ebfcToomas Soome        if (ptr[0] > MAX_DOMAIN_LABEL || next >= d->c + MAX_DOMAIN_NAME) return mDNSNULL;
607c65ebfcToomas Soome        if (IsHighASCIILabel(ptr)) ans = ptr;
608c65ebfcToomas Soome        ptr = next;
609c65ebfcToomas Soome    }
610c65ebfcToomas Soome    return ans;
611c65ebfcToomas Soome}
612c65ebfcToomas Soome
613c65ebfcToomas SoomemDNSlocal mDNSBool PerformNextPunycodeConversion(const DNSQuestion *const q, domainname *const newname)
614c65ebfcToomas Soome{
615c65ebfcToomas Soome    const mDNSu8 *h = FindLastHighASCIILabel(&q->qname);
616c65ebfcToomas Soome    #if DEBUG_PUNYCODE
617c65ebfcToomas Soome    LogMsg("PerformNextPunycodeConversion: %##s (%s) Last High-ASCII Label %##s", q->qname.c, DNSTypeName(q->qtype), h);
618c65ebfcToomas Soome    #endif
619c65ebfcToomas Soome    if (!h) return mDNSfalse;  // There are no high-ascii labels to convert
620c65ebfcToomas Soome
621c65ebfcToomas Soome    mDNSu8 *const dst = PunycodeConvert(h, newname->c + (h - q->qname.c), newname->c + MAX_DOMAIN_NAME);
622c65ebfcToomas Soome    if (!dst)
623c65ebfcToomas Soome        return mDNSfalse;  // The label was not convertible to Punycode
624c65ebfcToomas Soome    else
625c65ebfcToomas Soome    {
626c65ebfcToomas Soome        // If Punycode conversion of final eligible label was successful, copy the rest of the domainname
627c65ebfcToomas Soome        const mDNSu8 *const src = h + 1 + h[0];
628c65ebfcToomas Soome        const mDNSu8 remainder  = DomainNameLength((domainname*)src);
629c65ebfcToomas Soome        if (dst + remainder > newname->c + MAX_DOMAIN_NAME) return mDNSfalse;  // Name too long -- cannot be converted to Punycode
630c65ebfcToomas Soome
631c65ebfcToomas Soome        mDNSPlatformMemCopy(newname->c, q->qname.c, h - q->qname.c);  // Fill in the leading part
632c65ebfcToomas Soome        mDNSPlatformMemCopy(dst, src, remainder);                     // Fill in the trailing part
633c65ebfcToomas Soome        #if DEBUG_PUNYCODE
634c65ebfcToomas Soome        LogMsg("PerformNextPunycodeConversion: %##s converted to %##s", q->qname.c, newname->c);
635c65ebfcToomas Soome        #endif
636c65ebfcToomas Soome        return mDNStrue;
637c65ebfcToomas Soome    }
638c65ebfcToomas Soome}
639c65ebfcToomas Soome
640c65ebfcToomas Soome#endif // USE_LIBIDN
641c65ebfcToomas Soome
6425ffb0c9Toomas Soome// For a single given DNSQuestion pointed to by CurrentQuestion, deliver an add/remove result for the single given AuthRecord
6435ffb0c9Toomas Soome// Note: All the callers should use the m->CurrentQuestion to see if the question is still valid or not
6445ffb0c9Toomas SoomemDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
6455ffb0c9Toomas Soome{
6465ffb0c9Toomas Soome    DNSQuestion *q = m->CurrentQuestion;
6475ffb0c9Toomas Soome    mDNSBool followcname;
6485ffb0c9Toomas Soome
6495ffb0c9Toomas Soome    if (!q)
6505ffb0c9Toomas Soome    {
6515ffb0c9Toomas Soome        LogMsg("AnswerLocalQuestionWithLocalAuthRecord: ERROR!! CurrentQuestion NULL while answering with %s", ARDisplayString(m, rr));
6525ffb0c9Toomas Soome        return;
6535ffb0c9Toomas Soome    }
6545ffb0c9Toomas Soome
6555ffb0c9Toomas Soome    followcname = FollowCNAME(q, &rr->resrec, AddRecord);
6565ffb0c9Toomas Soome
6575ffb0c9Toomas Soome    // We should not be delivering results for record types Unregistered, Deregistering, and (unverified) Unique
6585ffb0c9Toomas Soome    if (!(rr->resrec.RecordType & kDNSRecordTypeActiveMask))
6595ffb0c9Toomas Soome    {
6605ffb0c9Toomas Soome        LogMsg("AnswerLocalQuestionWithLocalAuthRecord: *NOT* delivering %s event for local record type %X %s",
6615ffb0c9Toomas Soome               AddRecord ? "Add" : "Rmv", rr->resrec.RecordType, ARDisplayString(m, rr));
6625ffb0c9Toomas Soome        return;
6635ffb0c9Toomas Soome    }
6645ffb0c9Toomas Soome
6655ffb0c9Toomas Soome    // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
6665ffb0c9Toomas Soome    if (AddRecord) rr->AnsweredLocalQ = mDNStrue;
6675ffb0c9Toomas Soome    mDNS_DropLockBeforeCallback();      // Allow client to legally make mDNS API calls from the callback
6685ffb0c9Toomas Soome    if (q->QuestionCallback && !q->NoAnswer)
6695ffb0c9Toomas Soome    {
6705ffb0c9Toomas Soome        q->CurrentAnswers += AddRecord ? 1 : -1;
6715ffb0c9Toomas Soome        if (UniqueLocalOnlyRecord(rr))
6725ffb0c9Toomas Soome        {
6735ffb0c9Toomas Soome            if (!followcname || q->ReturnIntermed)
6745ffb0c9Toomas Soome            {
6755ffb0c9Toomas Soome                // Don't send this packet on the wire as we answered from /etc/hosts
6765ffb0c9Toomas Soome                q->ThisQInterval = 0;
6775ffb0c9Toomas Soome                q->LOAddressAnswers += AddRecord ? 1 : -1;
6785ffb0c9Toomas Soome                q->QuestionCallback(m, q, &rr->resrec, AddRecord);
6795ffb0c9Toomas Soome            }
6805ffb0c9Toomas Soome            mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
6815ffb0c9Toomas Soome            // The callback above could have caused the question to stop. Detect that
6825ffb0c9Toomas Soome            // using m->CurrentQuestion
6835ffb0c9Toomas Soome            if (followcname && m->CurrentQuestion == q)
6845ffb0c9Toomas Soome                AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
6855ffb0c9Toomas Soome            return;
6865ffb0c9Toomas Soome        }
6875ffb0c9Toomas Soome        else
6885ffb0c9Toomas Soome        {
6895ffb0c9Toomas Soome            q->QuestionCallback(m, q, &rr->resrec, AddRecord);
6905ffb0c9Toomas Soome        }
6915ffb0c9Toomas Soome    }
6925ffb0c9Toomas Soome    mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
6935ffb0c9Toomas Soome}
6945ffb0c9Toomas Soome
6955ffb0c9Toomas SoomemDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
6965ffb0c9Toomas Soome{
6975ffb0c9Toomas Soome    if (m->CurrentQuestion)
6985ffb0c9Toomas Soome        LogMsg("AnswerInterfaceAnyQuestionsWithLocalAuthRecord: ERROR m->CurrentQuestion already set: %##s (%s)",
6995ffb0c9Toomas Soome               m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
7005ffb0c9Toomas Soome    m->CurrentQuestion = m->Questions;
7015ffb0c9Toomas Soome    while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
7025ffb0c9Toomas Soome    {
7035ffb0c9Toomas Soome        mDNSBool answered;
7045ffb0c9Toomas Soome        DNSQuestion *q = m->CurrentQuestion;
7055ffb0c9Toomas Soome        if (RRAny(rr))
7065ffb0c9Toomas Soome            answered = ResourceRecordAnswersQuestion(&rr->resrec, q);
7075ffb0c9Toomas Soome        else
7085ffb0c9Toomas Soome            answered = LocalOnlyRecordAnswersQuestion(rr, q);
7095ffb0c9Toomas Soome        if (answered)
7105ffb0c9Toomas Soome            AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord);       // MUST NOT dereference q again
7115ffb0c9Toomas Soome        if (m->CurrentQuestion == q)    // If m->CurrentQuestion was not auto-advanced, do it ourselves now
7125ffb0c9Toomas Soome            m->CurrentQuestion = q->next;
7135ffb0c9Toomas Soome    }
7145ffb0c9Toomas Soome    m->CurrentQuestion = mDNSNULL;
7155ffb0c9Toomas Soome}
7165ffb0c9Toomas Soome
7175ffb0c9Toomas Soome// When a new local AuthRecord is created or deleted, AnswerAllLocalQuestionsWithLocalAuthRecord()
7185ffb0c9Toomas Soome// delivers the appropriate add/remove events to listening questions:
7195ffb0c9Toomas Soome// 1. It runs though all our LocalOnlyQuestions delivering answers as appropriate,
7205ffb0c9Toomas Soome//    stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
7215ffb0c9Toomas Soome// 2. If the AuthRecord is marked mDNSInterface_LocalOnly or mDNSInterface_P2P, then it also runs though
7225ffb0c9Toomas Soome//    our main question list, delivering answers to mDNSInterface_Any questions as appropriate,
7235ffb0c9Toomas Soome//    stopping if it reaches a NewQuestion -- brand-new questions are handled by AnswerNewQuestion().
7245ffb0c9Toomas Soome//
7255ffb0c9Toomas Soome// AnswerAllLocalQuestionsWithLocalAuthRecord is used by the m->NewLocalRecords loop in mDNS_Execute(),
7265ffb0c9Toomas Soome// and by mDNS_Deregister_internal()
7275ffb0c9Toomas Soome
7285ffb0c9Toomas SoomemDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
7295ffb0c9Toomas Soome{
7305ffb0c9Toomas Soome    if (m->CurrentQuestion)
7315ffb0c9Toomas Soome        LogMsg("AnswerAllLocalQuestionsWithLocalAuthRecord ERROR m->CurrentQuestion already set: %##s (%s)",
7325ffb0c9Toomas Soome               m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
7335ffb0c9Toomas Soome
7345ffb0c9Toomas Soome    m->CurrentQuestion = m->LocalOnlyQuestions;
7355ffb0c9Toomas Soome    while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
7365ffb0c9Toomas Soome    {
7375ffb0c9Toomas Soome        mDNSBool answered;
7385ffb0c9Toomas Soome        DNSQuestion *q = m->CurrentQuestion;
7395ffb0c9Toomas Soome        // We are called with both LocalOnly/P2P record or a regular AuthRecord
7405ffb0c9Toomas Soome        if (RRAny(rr))
7415ffb0c9Toomas Soome            answered = ResourceRecordAnswersQuestion(&rr->resrec, q);
7425ffb0c9Toomas Soome        else
7435ffb0c9Toomas Soome            answered = LocalOnlyRecordAnswersQuestion(rr, q);
7445ffb0c9Toomas Soome        if (answered)
7455ffb0c9Toomas Soome            AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord);           // MUST NOT dereference q again
7465ffb0c9Toomas Soome        if (m->CurrentQuestion == q)    // If m->CurrentQuestion was not auto-advanced, do it ourselves now
7475ffb0c9Toomas Soome            m->CurrentQuestion = q->next;
7485ffb0c9Toomas Soome    }
7495ffb0c9Toomas Soome
7505ffb0c9Toomas Soome    m->CurrentQuestion = mDNSNULL;
7515ffb0c9Toomas Soome
7525ffb0c9Toomas Soome    // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions
7535ffb0c9Toomas Soome    if (rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P)
7545ffb0c9Toomas Soome        AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, rr, AddRecord);
7555ffb0c9Toomas Soome
7565ffb0c9Toomas Soome}
7584b22b93rs// ***************************************************************************
7604b22b93rs#pragma mark -
7614b22b93rs#pragma mark - Resource Record Utility Functions
7644b22b93rs#define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
7665ffb0c9Toomas Soome#define ResourceRecordIsValidAnswer(RR) ( ((RR)->resrec.RecordType & kDNSRecordTypeActiveMask)  && \
7675ffb0c9Toomas Soome                                          ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
7685ffb0c9Toomas Soome                                          ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
7695ffb0c9Toomas Soome                                          ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask))  )
7714b22b93rs#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
7725ffb0c9Toomas Soome    (ResourceRecordIsValidAnswer(RR) && \
7735ffb0c9Toomas Soome     ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
7754b22b93rs#define DefaultProbeCountForTypeUnique ((mDNSu8)3)
7764b22b93rs#define DefaultProbeCountForRecordType(X)      ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
7785ffb0c9Toomas Soome// See RFC 6762: "8.3 Announcing"
7795ffb0c9Toomas Soome// "The Multicast DNS responder MUST send at least two unsolicited responses, one second apart."
7805ffb0c9Toomas Soome// Send 4, which is really 8 since we send on both IPv4 and IPv6.
7815ffb0c9Toomas Soome#define InitialAnnounceCount ((mDNSu8)4)
7825ffb0c9Toomas Soome
7835ffb0c9Toomas Soome// For goodbye packets we set the count to 3, and for wakeups we set it to 18
7845ffb0c9Toomas Soome// (which will be up to 15 wakeup attempts over the course of 30 seconds,
7855ffb0c9Toomas Soome// and then if the machine fails to wake, 3 goodbye packets).
7865ffb0c9Toomas Soome#define GoodbyeCount ((mDNSu8)3)
7875ffb0c9Toomas Soome#define WakeupCount ((mDNSu8)18)
7885ffb0c9Toomas Soome#define MAX_PROBE_RESTARTS ((mDNSu8)20)
7893b436d0Toomas Soome#define MAX_GHOST_TIME ((mDNSs32)((60*60*24*7)*mDNSPlatformOneSecond))  //  One week
7905ffb0c9Toomas Soome
7915ffb0c9Toomas Soome// Number of wakeups we send if WakeOnResolve is set in the question
7925ffb0c9Toomas Soome#define InitialWakeOnResolveCount ((mDNSu8)3)
7944b22b93rs// Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
7954b22b93rs// This means that because the announce interval is doubled after sending the first packet, the first
7964b22b93rs// observed on-the-wire inter-packet interval between announcements is actually one second.
7974b22b93rs// The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
7984b22b93rs#define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
7994b22b93rs#define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
8004b22b93rs#define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
8025ffb0c9Toomas Soome#define DefaultAPIntervalForRecordType(X)  ((X) &kDNSRecordTypeActiveSharedMask ? DefaultAnnounceIntervalForTypeShared : \
8035ffb0c9Toomas Soome                                            (X) &kDNSRecordTypeUnique           ? DefaultProbeIntervalForTypeUnique    : \
8045ffb0c9Toomas Soome                                            (X) &kDNSRecordTypeActiveUniqueMask ? DefaultAnnounceIntervalForTypeUnique : 0)
8064b22b93rs#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
8074b22b93rs#define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
8084b22b93rs#define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
8105ffb0c9Toomas Soome// Adjustment factor to avoid race condition (used for unicast cache entries) :
8115ffb0c9Toomas Soome// Suppose real record has TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100.
8125ffb0c9Toomas Soome// If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another
8135ffb0c9Toomas Soome// 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox.
8145ffb0c9Toomas Soome// To avoid this, we extend the record's effective TTL to give it a little extra grace period.
8155ffb0c9Toomas Soome// We adjust the 100 second TTL to 127. This means that when we do our 80% query at 102 seconds,
8165ffb0c9Toomas Soome// the cached copy at our local caching server will already have expired, so the server will be forced
8175ffb0c9Toomas Soome// to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds.
8185ffb0c9Toomas Soome
8195ffb0c9Toomas Soome#define RRAdjustTTL(ttl) ((ttl) + ((ttl)/4) + 2)
8205ffb0c9Toomas Soome#define RRUnadjustedTTL(ttl) ((((ttl) - 2) * 4) / 5)
8215ffb0c9Toomas Soome
8224b22b93rs#define MaxUnansweredQueries 4
8244b22b93rs// SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
8254b22b93rs// (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
8264b22b93rs// TTL and rdata may differ.
8274b22b93rs// This is used for cache flush management:
8284b22b93rs// When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
8294b22b93rs// When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
8305ffb0c9Toomas Soome
8315ffb0c9Toomas Soome// SameResourceRecordNameClassInterface is functionally the same as SameResourceRecordSignature, except rrtype does not have to match
8325ffb0c9Toomas Soome
8335ffb0c9Toomas Soome#define SameResourceRecordSignature(A,B) (A)->resrec.rrtype == (B)->resrec.rrtype && SameResourceRecordNameClassInterface((A),(B))
8345ffb0c9Toomas Soome
8355ffb0c9Toomas SoomemDNSlocal mDNSBool SameResourceRecordNameClassInterface(const AuthRecord *const r1, const AuthRecord *const r2)
8365ffb0c9Toomas Soome{
8375ffb0c9Toomas Soome    if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
8385ffb0c9Toomas Soome    if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
8395ffb0c9Toomas Soome    if (r1->resrec.InterfaceID &&
8405ffb0c9Toomas Soome        r2->resrec.InterfaceID &&
8415ffb0c9Toomas Soome        r1->resrec.InterfaceID != r2->resrec.InterfaceID) return(mDNSfalse);
8425ffb0c9Toomas Soome    return (mDNSBool)(
8435ffb0c9Toomas Soome               r1->resrec.rrclass  == r2->resrec.rrclass &&
8445ffb0c9Toomas Soome               r1->resrec.namehash == r2->resrec.namehash &&
8455ffb0c9Toomas Soome               SameDomainName(r1->resrec.name, r2->resrec.name));
8465ffb0c9Toomas Soome}
8484b22b93rs// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
8494b22b93rs// authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
8504b22b93rs// complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
8514b22b93rs// In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
8524b22b93rs// so a response of any type should match, even if it is not actually the type the client plans to use.
8535ffb0c9Toomas Soome
8545ffb0c9Toomas Soome// For now, to make it easier to avoid false conflicts, we treat SPS Proxy records like shared records,
8555ffb0c9Toomas Soome// and require the rrtypes to match for the rdata to be considered potentially conflicting
8564b22b93rsmDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr)
8575ffb0c9Toomas Soome{
8585ffb0c9Toomas Soome    if (!pktrr)  { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); }
8595ffb0c9Toomas Soome    if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); }
8605ffb0c9Toomas Soome    if (pktrr->resrec.InterfaceID &&
8615ffb0c9Toomas Soome        authrr->resrec.InterfaceID &&
8625ffb0c9Toomas Soome        pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse);
8635ffb0c9Toomas Soome    if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) || authrr->WakeUp.HMAC.l[0])
8645ffb0c9Toomas Soome        if (pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse);
865c65ebfcToomas Soome    if ((authrr->resrec.InterfaceID == mDNSInterface_Any) &&
866c65ebfcToomas Soome        !mDNSPlatformValidRecordForInterface(authrr, pktrr->resrec.InterfaceID)) return(mDNSfalse);
8675ffb0c9Toomas Soome    return (mDNSBool)(
8685ffb0c9Toomas Soome               pktrr->resrec.rrclass == authrr->resrec.rrclass &&
8695ffb0c9Toomas Soome               pktrr->resrec.namehash == authrr->resrec.namehash &&
8705ffb0c9Toomas Soome               SameDomainName(pktrr->resrec.name, authrr->resrec.name));
8715ffb0c9Toomas Soome}
8725ffb0c9Toomas Soome
8735ffb0c9Toomas Soome// CacheRecord *ka is the CacheRecord from the known answer list in the query.
8744b22b93rs// This is the information that the requester believes to be correct.
8754b22b93rs// AuthRecord *rr is the answer we are proposing to give, if not suppressed.
8764b22b93rs// This is the information that we believe to be correct.
8774b22b93rs// We've already determined that we plan to give this answer on this interface
8784b22b93rs// (either the record is non-specific, or it is specific to this interface)
8794b22b93rs// so now we just need to check the name, type, class, rdata and TTL.
8804b22b93rsmDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr)
8815ffb0c9Toomas Soome{
8825ffb0c9Toomas Soome    // If RR signature is different, or data is different, then don't suppress our answer
8835ffb0c9Toomas Soome    if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse);
8845ffb0c9Toomas Soome
8855ffb0c9Toomas Soome    // If the requester's indicated TTL is less than half the real TTL,
8865ffb0c9Toomas Soome    // we need to give our answer before the requester's copy expires.
8875ffb0c9Toomas Soome    // If the requester's indicated TTL is at least half the real TTL,
8885ffb0c9Toomas Soome    // then we can suppress our answer this time.
8895ffb0c9Toomas Soome    // If the requester's indicated TTL is greater than the TTL we believe,
8905ffb0c9Toomas Soome    // then that's okay, and we don't need to do anything about it.
8915ffb0c9Toomas Soome    // (If two responders on the network are offering the same information,
8925ffb0c9Toomas Soome    // that's okay, and if they are offering the information with different TTLs,
8935ffb0c9Toomas Soome    // the one offering the lower TTL should defer to the one offering the higher TTL.)
8945ffb0c9Toomas Soome    return (mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2);
8955ffb0c9Toomas Soome}
8974b22b93rsmDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
8985ffb0c9Toomas Soome{
8995ffb0c9Toomas Soome    if (rr->resrec.RecordType == kDNSRecordTypeUnique)
9005ffb0c9Toomas Soome    {
9015ffb0c9Toomas Soome        if ((rr->LastAPTime + rr->ThisAPInterval) - m->timenow > mDNSPlatformOneSecond * 10)
9025ffb0c9Toomas Soome        {
9035ffb0c9Toomas Soome            LogMsg("SetNextAnnounceProbeTime: ProbeCount %d Next in %d %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
9045ffb0c9Toomas Soome            LogMsg("SetNextAnnounceProbeTime: m->SuppressProbes %d m->timenow %d diff %d", m->SuppressProbes, m->timenow, m->SuppressProbes - m->timenow);
9055ffb0c9Toomas Soome        }
9065ffb0c9Toomas Soome        if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
9075ffb0c9Toomas Soome            m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
9085ffb0c9Toomas Soome        // Some defensive code:
9095ffb0c9Toomas Soome        // If (rr->LastAPTime + rr->ThisAPInterval) happens to be far in the past, we don't want to allow
9105ffb0c9Toomas Soome        // NextScheduledProbe to be set excessively in the past, because that can cause bad things to happen.
9115ffb0c9Toomas Soome        // See: <rdar://problem/7795434> mDNS: Sometimes advertising stops working and record interval is set to zero
9125ffb0c9Toomas Soome        if (m->NextScheduledProbe - m->timenow < 0)
9135ffb0c9Toomas Soome            m->NextScheduledProbe = m->timenow;
9145ffb0c9Toomas Soome    }
9155ffb0c9Toomas Soome    else if (rr->AnnounceCount && (ResourceRecordIsValidAnswer(rr) || rr->resrec.RecordType == kDNSRecordTypeDeregistering))
9165ffb0c9Toomas Soome    {
9175ffb0c9Toomas Soome        if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
9185ffb0c9Toomas Soome            m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
9195ffb0c9Toomas Soome    }
9205ffb0c9Toomas Soome}
9224b22b93rsmDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
9235ffb0c9Toomas Soome{
9245ffb0c9Toomas Soome    // For reverse-mapping Sleep Proxy PTR records, probe interval is one second
9255ffb0c9Toomas Soome    rr->ThisAPInterval = rr->AddressProxy.type ? mDNSPlatformOneSecond : DefaultAPIntervalForRecordType(rr->resrec.RecordType);
9265ffb0c9Toomas Soome
9275ffb0c9Toomas Soome    // * If this is a record type that's going to probe, then we use the m->SuppressProbes time.
9285ffb0c9Toomas Soome    // * Otherwise, if it's not going to probe, but m->SuppressProbes is set because we have other
9295ffb0c9Toomas Soome    //   records that are going to probe, then we delay its first announcement so that it will
9305ffb0c9Toomas Soome    //   go out synchronized with the first announcement for the other records that *are* probing.
9315ffb0c9Toomas Soome    //   This is a minor performance tweak that helps keep groups of related records synchronized together.
9325ffb0c9Toomas Soome    //   The addition of "interval / 2" is to make sure that, in the event that any of the probes are
9335ffb0c9Toomas Soome    //   delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
9345ffb0c9Toomas Soome    //   When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
9355ffb0c9Toomas Soome    //   because they will meet the criterion of being at least half-way to their scheduled announcement time.
9365ffb0c9Toomas Soome    // * If it's not going to probe and m->SuppressProbes is not already set then we should announce immediately.
9375ffb0c9Toomas Soome
9385ffb0c9Toomas Soome    if (rr->ProbeCount)
9395ffb0c9Toomas Soome    {
9405ffb0c9Toomas Soome        // If we have no probe suppression time set, or it is in the past, set it now
9415ffb0c9Toomas Soome        if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0)
9425ffb0c9Toomas Soome        {
9435ffb0c9Toomas Soome            // To allow us to aggregate probes when a group of services are registered together,
944c65ebfcToomas Soome            // the first probe is delayed by a random delay in the range 1/8 to 1/4 second.
945c65ebfcToomas Soome            // This means the common-case behaviour is:
946c65ebfcToomas Soome            // randomized wait; probe
9475ffb0c9Toomas Soome            // 1/4 second wait; probe
9485ffb0c9Toomas Soome            // 1/4 second wait; probe
949c65ebfcToomas Soome            // 1/4 second wait; announce (i.e. service is normally announced 7/8 to 1 second after being registered)
9505ffb0c9Toomas Soome            m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2));
9515ffb0c9Toomas Soome
9525ffb0c9Toomas Soome            // If we already have a *probe* scheduled to go out sooner, then use that time to get better aggregation
9535ffb0c9Toomas Soome            if (m->SuppressProbes - m->NextScheduledProbe >= 0)
9545ffb0c9Toomas Soome                m->SuppressProbes = NonZeroTime(m->NextScheduledProbe);
9555ffb0c9Toomas Soome            if (m->SuppressProbes - m->timenow < 0)     // Make sure we don't set m->SuppressProbes excessively in the past
9565ffb0c9Toomas Soome                m->SuppressProbes = m->timenow;
9575ffb0c9Toomas Soome
9585ffb0c9Toomas Soome            // If we already have a *query* scheduled to go out sooner, then use that time to get better aggregation
9595ffb0c9Toomas Soome            if (m->SuppressProbes - m->NextScheduledQuery >= 0)
9605ffb0c9Toomas Soome                m->SuppressProbes = NonZeroTime(m->NextScheduledQuery);
9615ffb0c9Toomas Soome            if (m->SuppressProbes - m->timenow < 0)     // Make sure we don't set m->SuppressProbes excessively in the past
9625ffb0c9Toomas Soome                m->SuppressProbes = m->timenow;
9635ffb0c9Toomas Soome
9645ffb0c9Toomas Soome            // except... don't expect to be able to send before the m->SuppressSending timer fires
9655ffb0c9Toomas Soome            if (m->SuppressSending && m->SuppressProbes - m->SuppressSending < 0)
9665ffb0c9Toomas Soome                m->SuppressProbes = NonZeroTime(m->SuppressSending);
9675ffb0c9Toomas Soome
9685ffb0c9Toomas Soome            if (m->SuppressProbes - m->timenow > mDNSPlatformOneSecond * 8)
9695ffb0c9Toomas Soome            {
9705ffb0c9Toomas Soome                LogMsg("InitializeLastAPTime ERROR m->SuppressProbes %d m->NextScheduledProbe %d m->NextScheduledQuery %d m->SuppressSending %d %d",
9715ffb0c9Toomas Soome                       m->SuppressProbes     - m->timenow,
9725ffb0c9Toomas Soome                       m->NextScheduledProbe - m->timenow,
9735ffb0c9Toomas Soome                       m->NextScheduledQuery - m->timenow,
9745ffb0c9Toomas Soome                       m->SuppressSending,
9755ffb0c9Toomas Soome                       m->SuppressSending    - m->timenow);
9765ffb0c9Toomas Soome                m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2));
9775ffb0c9Toomas Soome            }
9785ffb0c9Toomas Soome        }
9795ffb0c9Toomas Soome        rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval;
9805ffb0c9Toomas Soome    }
981c65ebfcToomas Soome    // Skip kDNSRecordTypeKnownUnique and kDNSRecordTypeShared records here and set their LastAPTime in the "else" block below so
982c65ebfcToomas Soome    // that they get announced immediately, otherwise, their announcement would be delayed until the based on the SuppressProbes value.
983c65ebfcToomas Soome    else if ((rr->resrec.RecordType != kDNSRecordTypeKnownUnique) && (rr->resrec.RecordType != kDNSRecordTypeShared) && m->SuppressProbes && (m->SuppressProbes - m->timenow >= 0))
9845ffb0c9Toomas Soome        rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2;
9855ffb0c9Toomas Soome    else
9865ffb0c9Toomas Soome        rr->LastAPTime = m->timenow - rr->ThisAPInterval;
9875ffb0c9Toomas Soome
9885ffb0c9Toomas Soome    // For reverse-mapping Sleep Proxy PTR records we don't want to start probing instantly -- we
9895ffb0c9Toomas Soome    // wait one second to give the client a chance to go to sleep, and then start our ARP/NDP probing.
9905ffb0c9Toomas Soome    // After three probes one second apart with no answer, we conclude the client is now sleeping
9915ffb0c9Toomas Soome    // and we can begin broadcasting our announcements to take over ownership of that IP address.
9925ffb0c9Toomas Soome    // If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk
9935ffb0c9Toomas Soome    // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address.
994c65ebfcToomas Soome    if (rr->AddressProxy.type)
9955ffb0c9Toomas Soome        rr->LastAPTime = m->timenow;
9965ffb0c9Toomas Soome
9975ffb0c9Toomas Soome    // Set LastMCTime to now, to inhibit multicast responses
9985ffb0c9Toomas Soome    // (no need to send additional multicast responses when we're announcing anyway)
9995ffb0c9Toomas Soome    rr->LastMCTime      = m->timenow;
10005ffb0c9Toomas Soome    rr->LastMCInterface = mDNSInterfaceMark;
10015ffb0c9Toomas Soome
10025ffb0c9Toomas Soome    SetNextAnnounceProbeTime(m, rr);
10035ffb0c9Toomas Soome}
10045ffb0c9Toomas Soome
10055ffb0c9Toomas SoomemDNSlocal const domainname *SetUnicastTargetToHostName(mDNS *const m, AuthRecord *rr)
10065ffb0c9Toomas Soome{
10075ffb0c9Toomas Soome    const domainname *target;
10085ffb0c9Toomas Soome    if (rr->AutoTarget)
10095ffb0c9Toomas Soome    {
10105ffb0c9Toomas Soome        // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other
10115ffb0c9Toomas Soome        // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate,
10125ffb0c9Toomas Soome        // with the port number in our advertised SRV record automatically tracking the external mapped port.
10135ffb0c9Toomas Soome        DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name);
10145ffb0c9Toomas Soome        if (!AuthInfo || !AuthInfo->AutoTunnel) rr->AutoTarget = Target_AutoHostAndNATMAP;
10155ffb0c9Toomas Soome    }
10165ffb0c9Toomas Soome
10175ffb0c9Toomas Soome    target = GetServiceTarget(m, rr);
10185ffb0c9Toomas Soome    if (!target || target->c[0] == 0)
10195ffb0c9Toomas Soome    {
10205ffb0c9Toomas Soome        // defer registration until we've got a target
10215ffb0c9Toomas Soome        LogInfo("SetUnicastTargetToHostName No target for %s", ARDisplayString(m, rr));
10225ffb0c9Toomas Soome        rr->state = regState_NoTarget;
10235ffb0c9Toomas Soome        return mDNSNULL;
10245ffb0c9Toomas Soome    }
10255ffb0c9Toomas Soome    else
10265ffb0c9Toomas Soome    {
10275ffb0c9Toomas Soome        LogInfo("SetUnicastTargetToHostName target %##s for resource record %s", target->c, ARDisplayString(m,rr));
10285ffb0c9Toomas Soome        return target;
10295ffb0c9Toomas Soome    }
10305ffb0c9Toomas Soome}
10315ffb0c9Toomas Soome
10325ffb0c9Toomas Soome// Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname
10335ffb0c9Toomas Soome// Eventually we should unify this with GetServiceTarget() in uDNS.c
10344b22b93rsmDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
10355ffb0c9Toomas Soome{
10365ffb0c9Toomas Soome    domainname *const target = GetRRDomainNameTarget(&rr->resrec);
10375ffb0c9Toomas Soome    const domainname *newname = &m->MulticastHostname;
10385ffb0c9Toomas Soome
10395ffb0c9Toomas Soome    if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype));
10405ffb0c9Toomas Soome
10415ffb0c9Toomas Soome    if (!(rr->ForceMCast || rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P || IsLocalDomain(&rr->namestorage)))
10425ffb0c9Toomas Soome    {
10435ffb0c9Toomas Soome        const domainname *const n = SetUnicastTargetToHostName(m, rr);
10445ffb0c9Toomas Soome        if (n) newname = n;
1045cda73f6Toomas Soome        else { if (target) target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; }
10465ffb0c9Toomas Soome    }
10475ffb0c9Toomas Soome
10485ffb0c9Toomas Soome    if (target && SameDomainName(target, newname))
10495ffb0c9Toomas Soome        debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
10505ffb0c9Toomas Soome
10515ffb0c9Toomas Soome    if (target && !SameDomainName(target, newname))
10525ffb0c9Toomas Soome    {
10535ffb0c9Toomas Soome        AssignDomainName(target, newname);
10545ffb0c9Toomas Soome        SetNewRData(&rr->resrec, mDNSNULL, 0);      // Update rdlength, rdestimate, rdatahash
10555ffb0c9Toomas Soome
10565ffb0c9Toomas Soome        // If we're in the middle of probing this record, we need to start again,
10575ffb0c9Toomas Soome        // because changing its rdata may change the outcome of the tie-breaker.
10585ffb0c9Toomas Soome        // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
10595ffb0c9Toomas Soome        rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
10605ffb0c9Toomas Soome
10615ffb0c9Toomas Soome        // If we've announced this record, we really should send a goodbye packet for the old rdata before
10625ffb0c9Toomas Soome        // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
10635ffb0c9Toomas Soome        // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
10645ffb0c9Toomas Soome        if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared)
10655ffb0c9Toomas Soome            debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating",
10665ffb0c9Toomas Soome                   rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
10675ffb0c9Toomas Soome
10685ffb0c9Toomas Soome        rr->AnnounceCount  = InitialAnnounceCount;
10695ffb0c9Toomas Soome        rr->RequireGoodbye = mDNSfalse;
10705ffb0c9Toomas Soome        rr->ProbeRestartCount = 0;
10715ffb0c9Toomas Soome        InitializeLastAPTime(m, rr);
10725ffb0c9Toomas Soome    }
10735ffb0c9Toomas Soome}
10754b22b93rsmDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
10765ffb0c9Toomas Soome{
10775ffb0c9Toomas Soome    if (rr->RecordCallback)
10785ffb0c9Toomas Soome    {
10795ffb0c9Toomas Soome        // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
10805ffb0c9Toomas Soome        // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
10815ffb0c9Toomas Soome        rr->Acknowledged = mDNStrue;
10825ffb0c9Toomas Soome        mDNS_DropLockBeforeCallback();      // Allow client to legally make mDNS API calls from the callback
10835ffb0c9Toomas Soome        rr->RecordCallback(m, rr, mStatus_NoError);
10845ffb0c9Toomas Soome        mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
10855ffb0c9Toomas Soome    }
10865ffb0c9Toomas Soome}
10875ffb0c9Toomas Soome
10885ffb0c9Toomas SoomemDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr)
10895ffb0c9Toomas Soome{
10905ffb0c9Toomas Soome    // Make sure that we don't activate the SRV record and associated service records, if it is in
10915ffb0c9Toomas Soome    // NoTarget state. First time when a service is being instantiated, SRV record may be in NoTarget state.
10925ffb0c9Toomas Soome    // We should not activate any of the other reords (PTR, TXT) that are part of the service. When
10935ffb0c9Toomas Soome    // the target becomes available, the records will be reregistered.
10945ffb0c9Toomas Soome    if (rr->resrec.rrtype != kDNSType_SRV)
10955ffb0c9Toomas Soome    {
10965ffb0c9Toomas Soome        AuthRecord *srvRR = mDNSNULL;
10975ffb0c9Toomas Soome        if (rr->resrec.rrtype == kDNSType_PTR)
10985ffb0c9Toomas Soome            srvRR = rr->Additional1;
10995ffb0c9Toomas Soome        else if (rr->resrec.rrtype == kDNSType_TXT)
11005ffb0c9Toomas Soome            srvRR = rr->DependentOn;
11015ffb0c9Toomas Soome        if (srvRR)
11025ffb0c9Toomas Soome        {
11035ffb0c9Toomas Soome            if (srvRR->resrec.rrtype != kDNSType_SRV)
11045ffb0c9Toomas Soome            {
11055ffb0c9Toomas Soome                LogMsg("ActivateUnicastRegistration: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR));
11065ffb0c9Toomas Soome            }
11075ffb0c9Toomas Soome            else
11085ffb0c9Toomas Soome            {
11095ffb0c9Toomas Soome                LogInfo("ActivateUnicastRegistration: Found Service Record %s in state %d for %##s (%s)",
11105ffb0c9Toomas Soome                        ARDisplayString(m, srvRR), srvRR->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
11115ffb0c9Toomas Soome                rr->state = srvRR->state;
11125ffb0c9Toomas Soome            }
11135ffb0c9Toomas Soome        }
11145ffb0c9Toomas Soome    }
11155ffb0c9Toomas Soome
11165ffb0c9Toomas Soome    if (rr->state == regState_NoTarget)
11175ffb0c9Toomas Soome    {
11185ffb0c9Toomas Soome        LogInfo("ActivateUnicastRegistration record %s in regState_NoTarget, not activating", ARDisplayString(m, rr));
11195ffb0c9Toomas Soome        return;
11205ffb0c9Toomas Soome    }
11215ffb0c9Toomas Soome    // When we wake up from sleep, we call ActivateUnicastRegistration. It is possible that just before we went to sleep,
11225ffb0c9Toomas Soome    // the service/record was being deregistered. In that case, we should not try to register again. For the cases where
11235ffb0c9Toomas Soome    // the records are deregistered due to e.g., no target for the SRV record, we would have returned from above if it
11245ffb0c9Toomas Soome    // was already in NoTarget state. If it was in the process of deregistration but did not complete fully before we went
11255ffb0c9Toomas Soome    // 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.
11265ffb0c9Toomas Soome    if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
11275ffb0c9Toomas Soome    {
11285ffb0c9Toomas Soome        LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to DeregPending", ARDisplayString(m, rr), rr->state);
11295ffb0c9Toomas Soome        rr->state = regState_DeregPending;
11305ffb0c9Toomas Soome    }
11315ffb0c9Toomas Soome    else
11325ffb0c9Toomas Soome    {
11335ffb0c9Toomas Soome        LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to Pending", ARDisplayString(m, rr), rr->state);
11345ffb0c9Toomas Soome        rr->state = regState_Pending;
11355ffb0c9Toomas Soome    }
11365ffb0c9Toomas Soome    rr->ProbeCount     = 0;
11375ffb0c9Toomas Soome    rr->ProbeRestartCount = 0;
11385ffb0c9Toomas Soome    rr->AnnounceCount  = 0;
11395ffb0c9Toomas Soome    rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
11405ffb0c9Toomas Soome    rr->LastAPTime     = m->timenow - rr->ThisAPInterval;
11415ffb0c9Toomas Soome    rr->expire         = 0; // Forget about all the leases, start fresh
11425ffb0c9Toomas Soome    rr->uselease       = mDNStrue;
11435ffb0c9Toomas Soome    rr->updateid       = zeroID;
11445ffb0c9Toomas Soome    rr->SRVChanged     = mDNSfalse;
11455ffb0c9Toomas Soome    rr->updateError    = mStatus_NoError;
11465ffb0c9Toomas Soome    // RestartRecordGetZoneData calls this function whenever a new interface gets registered with core.
11475ffb0c9Toomas Soome    // The records might already be registered with the server and hence could have NAT state.
11485ffb0c9Toomas Soome    if (rr->NATinfo.clientContext)
11495ffb0c9Toomas Soome    {
11505ffb0c9Toomas Soome        mDNS_StopNATOperation_internal(m, &rr->NATinfo);
11515ffb0c9Toomas Soome        rr->NATinfo.clientContext = mDNSNULL;
11525ffb0c9Toomas Soome    }
11535ffb0c9Toomas Soome    if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; }
11545ffb0c9Toomas Soome    if (rr->tcp) { DisposeTCPConn(rr->tcp);       rr->tcp = mDNSNULL; }
11555ffb0c9Toomas Soome    if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
11565ffb0c9Toomas Soome        m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval);
11575ffb0c9Toomas Soome}
11585ffb0c9Toomas Soome
11595ffb0c9Toomas Soome// Two records qualify to be local duplicates if:
11605ffb0c9Toomas Soome// (a) the RecordTypes are the same, or
11615ffb0c9Toomas Soome// (b) one is Unique and the other Verified
11625ffb0c9Toomas Soome// (c) either is in the process of deregistering
11634b22b93rs#define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \
11645ffb0c9Toomas Soome                        ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified) || \
11655ffb0c9Toomas Soome                        ((A)->resrec.RecordType == kDNSRecordTypeDeregistering || (B)->resrec.RecordType == kDNSRecordTypeDeregistering))
11675ffb0c9Toomas Soome#define RecordIsLocalDuplicate(A,B) \
11685ffb0c9Toomas Soome    ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(& (A)->resrec, & (B)->resrec))
11695ffb0c9Toomas Soome
11705ffb0c9Toomas SoomemDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr)
11715ffb0c9Toomas Soome{
1172c65ebfcToomas Soome    const AuthGroup *a;
1173c65ebfcToomas Soome    AuthRecord *rp;
11745ffb0c9Toomas Soome
1175c65ebfcToomas Soome    a = AuthGroupForRecord(r, &rr->resrec);
11765ffb0c9Toomas Soome    if (!a) return mDNSNULL;
1177c65ebfcToomas Soome    rp = a->members;
1178c65ebfcToomas Soome    while (rp)
11795ffb0c9Toomas Soome    {
1180c65ebfcToomas Soome        if (!RecordIsLocalDuplicate(rp, rr))
1181c65ebfcToomas Soome            rp = rp->next;
11825ffb0c9Toomas Soome        else
11835ffb0c9Toomas Soome        {
1184c65ebfcToomas Soome            if (rp->resrec.RecordType == kDNSRecordTypeDeregistering)
11855ffb0c9Toomas Soome            {
1186c65ebfcToomas Soome                rp->AnnounceCount = 0;
1187c65ebfcToomas Soome                rp = rp->next;
11885ffb0c9Toomas Soome            }
1189c65ebfcToomas Soome            else return rp;
11905ffb0c9Toomas Soome        }
11915ffb0c9Toomas Soome    }
11925ffb0c9Toomas Soome    return (mDNSNULL);
11935ffb0c9Toomas Soome}
11945ffb0c9Toomas Soome
11955ffb0c9Toomas SoomemDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr)
11965ffb0c9Toomas Soome{
1197c65ebfcToomas Soome    const AuthGroup *a;
1198c65ebfcToomas Soome    const AuthRecord *rp;
11995ffb0c9Toomas Soome
1200c65ebfcToomas Soome    a = AuthGroupForRecord(r, &rr->resrec);
12015ffb0c9Toomas Soome    if (!a) return mDNSfalse;
1202c65ebfcToomas Soome    rp = a->members;
1203c65ebfcToomas Soome    while (rp)
12045ffb0c9Toomas Soome    {
12055ffb0c9Toomas Soome        const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
1206c65ebfcToomas Soome        const AuthRecord *s2 = rp->RRSet ? rp->RRSet : rp;
1207c65ebfcToomas Soome        if (s1 != s2 && SameResourceRecordSignature(rp, rr) && !IdenticalSameNameRecord(&rp->resrec, &rr->resrec))
12085ffb0c9Toomas Soome            return mDNStrue;
12095ffb0c9Toomas Soome        else
1210c65ebfcToomas Soome            rp = rp->next;
12115ffb0c9Toomas Soome    }
12125ffb0c9Toomas Soome    return (mDNSfalse);
12135ffb0c9Toomas Soome}
12145ffb0c9Toomas Soome
12155ffb0c9Toomas Soome// checks to see if "rr" is already present
12165ffb0c9Toomas SoomemDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr)
12175ffb0c9Toomas Soome{
1218c65ebfcToomas Soome    const AuthGroup *a;
1219c65ebfcToomas Soome    AuthRecord *rp;
12205ffb0c9Toomas Soome
1221c65ebfcToomas Soome    a = AuthGroupForRecord(r, &rr->resrec);
12225ffb0c9Toomas Soome    if (!a) return mDNSNULL;
1223c65ebfcToomas Soome    rp = a->members;
1224c65ebfcToomas Soome    while (rp)
12255ffb0c9Toomas Soome    {
1226c65ebfcToomas Soome        if (rp != rr)
1227c65ebfcToomas Soome            rp = rp->next;
12285ffb0c9Toomas Soome        else
12295ffb0c9Toomas Soome        {
1230c65ebfcToomas Soome            return rp;
12315ffb0c9Toomas Soome        }
12325ffb0c9Toomas Soome    }
12335ffb0c9Toomas Soome    return (mDNSNULL);
12345ffb0c9Toomas Soome}
12355ffb0c9Toomas Soome
12365ffb0c9Toomas Soome
12375ffb0c9Toomas SoomemDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr)
12385ffb0c9Toomas Soome{
1239cda73f6Toomas Soome    if (RRLocalOnly(rr))
1240cda73f6Toomas Soome    {
1241cda73f6Toomas Soome        // A sanity check, this should be prevented in calling code.
1242cda73f6Toomas Soome        LogInfo("DecrementAutoTargetServices: called for RRLocalOnly() record: %s", ARDisplayString(m, rr));
1243cda73f6Toomas Soome        return;
1244cda73f6Toomas Soome    }
1245cda73f6Toomas Soome
12465ffb0c9Toomas Soome    if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost)
12475ffb0c9Toomas Soome    {
1248cda73f6Toomas Soome        // If about to get rid of the last advertised service
1249cda73f6Toomas Soome        if (m->AutoTargetServices == 1)
12505ffb0c9Toomas Soome            DeadvertiseAllInterfaceRecords(m);
1251cda73f6Toomas Soome
1252cda73f6Toomas Soome        m->AutoTargetServices--;
1253cda73f6Toomas Soome        LogInfo("DecrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr));
1254cda73f6Toomas Soome    }
1255cda73f6Toomas Soome
1256c65ebfcToomas Soome#if BONJOUR_ON_DEMAND
1257cda73f6Toomas Soome    if (!AuthRecord_uDNS(rr))
1258cda73f6Toomas Soome    {
1259cda73f6Toomas Soome        if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1)
1260c65ebfcToomas Soome            m->NextBonjourDisableTime = NonZeroTime(m->timenow + (BONJOUR_DISABLE_DELAY * mDNSPlatformOneSecond));
1261cda73f6Toomas Soome        m->NumAllInterfaceRecords--;
1262cda73f6Toomas Soome        LogInfo("DecrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s",
1263cda73f6Toomas Soome            m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr));
12645ffb0c9Toomas Soome    }
1265c65ebfcToomas Soome#endif // BONJOUR_ON_DEMAND
12665ffb0c9Toomas Soome}
12675ffb0c9Toomas Soome
12685ffb0c9Toomas SoomemDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr)
12695ffb0c9Toomas Soome{
1270c65ebfcToomas Soome    mDNSBool enablingBonjour = 0;
1271c65ebfcToomas Soome
1272cda73f6Toomas Soome    if (RRLocalOnly(rr))
1273cda73f6Toomas Soome    {
1274cda73f6Toomas Soome        // A sanity check, this should be prevented in calling code.
1275cda73f6Toomas Soome        LogInfo("IncrementAutoTargetServices: called for RRLocalOnly() record: %s", ARDisplayString(m, rr));
1276cda73f6Toomas Soome        return;
1277cda73f6Toomas Soome    }
1278cda73f6Toomas Soome
1279c65ebfcToomas Soome#if BONJOUR_ON_DEMAND
1280cda73f6Toomas Soome    if (!AuthRecord_uDNS(rr))
12815ffb0c9Toomas Soome    {
1282cda73f6Toomas Soome        m->NumAllInterfaceRecords++;
1283cda73f6Toomas Soome        LogInfo("IncrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s",
1284cda73f6Toomas Soome            m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr));
1285cda73f6Toomas Soome        if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1)
1286c65ebfcToomas Soome        {
1287c65ebfcToomas Soome            m->NextBonjourDisableTime = 0;
1288c65ebfcToomas Soome            if (m->BonjourEnabled == 0)
1289c65ebfcToomas Soome            {
1290c65ebfcToomas Soome                // Enable Bonjour immediately by scheduling network changed processing where
1291c65ebfcToomas Soome                // we will join the multicast group on each active interface.
1292c65ebfcToomas Soome                m->BonjourEnabled = 1;
1293c65ebfcToomas Soome                enablingBonjour = 1;
1294c65ebfcToomas Soome                m->NetworkChanged = m->timenow;
1295c65ebfcToomas Soome            }
1296c65ebfcToomas Soome        }
1297cda73f6Toomas Soome    }
1298c65ebfcToomas Soome#endif // BONJOUR_ON_DEMAND
12995ffb0c9Toomas Soome
1300cda73f6Toomas Soome    if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost)
1301cda73f6Toomas Soome    {
13025ffb0c9Toomas Soome        m->AutoTargetServices++;
1303cda73f6Toomas Soome        LogInfo("IncrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr));
1304c65ebfcToomas Soome
1305c65ebfcToomas Soome        // If this is the first advertised service and we did not just enable Bonjour above, then
1306c65ebfcToomas Soome        // advertise all the interface records.  If we did enable Bonjour above, the interface records will
1307c65ebfcToomas Soome        // be advertised during the network changed processing scheduled above, so no need
1308c65ebfcToomas Soome        // to do it here.
1309c65ebfcToomas Soome        if ((m->AutoTargetServices == 1) && (enablingBonjour == 0))
13105ffb0c9Toomas Soome            AdvertiseAllInterfaceRecords(m);
13115ffb0c9Toomas Soome    }
13125ffb0c9Toomas Soome}
13135ffb0c9Toomas Soome
13145ffb0c9Toomas SoomemDNSlocal void getKeepaliveRaddr(mDNS *const m, AuthRecord *rr, mDNSAddr *raddr)
13155ffb0c9Toomas Soome{
1316c65ebfcToomas Soome    mDNSAddr     laddr = zeroAddr;
1317c65ebfcToomas Soome    mDNSEthAddr  eth = zeroEthAddr;
1318c65ebfcToomas Soome    mDNSIPPort   lport = zeroIPPort;
1319c65ebfcToomas Soome    mDNSIPPort   rport = zeroIPPort;
1320c65ebfcToomas Soome    mDNSu32      timeout = 0;
1321c65ebfcToomas Soome    mDNSu32      seq = 0;
1322c65ebfcToomas Soome    mDNSu32      ack = 0;
1323c65ebfcToomas Soome    mDNSu16      win = 0;
13245ffb0c9Toomas Soome
13255ffb0c9Toomas Soome    if (mDNS_KeepaliveRecord(&rr->resrec))
13265ffb0c9Toomas Soome    {
13275ffb0c9Toomas Soome        mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, raddr, &eth, &seq, &ack, &lport, &rport, &win);
13285ffb0c9Toomas Soome        if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(raddr) || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport))
13295ffb0c9Toomas Soome        {
13305ffb0c9Toomas Soome            LogMsg("getKeepaliveRaddr: not a valid record %s for keepalive %#a:%d %#a:%d", ARDisplayString(m, rr), &laddr, lport.NotAnInteger, raddr, rport.NotAnInteger);
13315ffb0c9Toomas Soome            return;
13325ffb0c9Toomas Soome        }
13335ffb0c9Toomas Soome    }
13345ffb0c9Toomas Soome}
13355ffb0c9Toomas Soome
13365ffb0c9Toomas Soome// Exported so uDNS.c can call this
13375ffb0c9Toomas SoomemDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
13385ffb0c9Toomas Soome{
13395ffb0c9Toomas Soome    domainname *target = GetRRDomainNameTarget(&rr->resrec);
13405ffb0c9Toomas Soome    AuthRecord *r;
13415ffb0c9Toomas Soome    AuthRecord **p = &m->ResourceRecords;
13425ffb0c9Toomas Soome    AuthRecord **d = &m->DuplicateRecords;
13435ffb0c9Toomas Soome
13445ffb0c9Toomas Soome    if ((mDNSs32)rr->resrec.rroriginalttl <= 0)
13455ffb0c9Toomas Soome    { LogMsg("mDNS_Register_internal: TTL %X should be 1 - 0x7FFFFFFF %s", rr->resrec.rroriginalttl, ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
13465ffb0c9Toomas Soome
13475ffb0c9Toomas Soome    if (!rr->resrec.RecordType)
13485ffb0c9Toomas Soome    { LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
13495ffb0c9Toomas Soome
13505ffb0c9Toomas Soome    if (m->ShutdownTime)
13515ffb0c9Toomas Soome    { LogMsg("mDNS_Register_internal: Shutting down, can't register %s", ARDisplayString(m, rr)); return(mStatus_ServiceNotRunning); }
13525ffb0c9Toomas Soome
13535ffb0c9Toomas Soome    if (m->DivertMulticastAdvertisements && !AuthRecord_uDNS(rr))
13545ffb0c9Toomas Soome    {
13555ffb0c9Toomas Soome        mDNSInterfaceID previousID = rr->resrec.InterfaceID;
13565ffb0c9Toomas Soome        if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P)
13575ffb0c9Toomas Soome        {
13585ffb0c9Toomas Soome            rr->resrec.InterfaceID = mDNSInterface_LocalOnly;
13595ffb0c9Toomas Soome            rr->ARType = AuthRecordLocalOnly;
13605ffb0c9Toomas Soome        }
13615ffb0c9Toomas Soome        if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
13625ffb0c9Toomas Soome        {
13635ffb0c9Toomas Soome            NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
13645ffb0c9Toomas Soome            if (intf && !intf->Advertise) { rr->resrec.InterfaceID = mDNSInterface_LocalOnly; rr->ARType = AuthRecordLocalOnly; }
13655ffb0c9Toomas Soome        }
13665ffb0c9Toomas Soome        if (rr->resrec.InterfaceID != previousID)
13675ffb0c9Toomas Soome            LogInfo("mDNS_Register_internal: Diverting record to local-only %s", ARDisplayString(m, rr));
13685ffb0c9Toomas Soome    }
13695ffb0c9Toomas Soome
13705ffb0c9Toomas Soome    if (RRLocalOnly(rr))
13715ffb0c9Toomas Soome    {
13725ffb0c9Toomas Soome        if (CheckAuthSameRecord(&m->rrauth, rr))
13735ffb0c9Toomas Soome        {
13745ffb0c9Toomas Soome            LogMsg("mDNS_Register_internal: ERROR!! Tried to register LocalOnly AuthRecord %p %##s (%s) that's already in the list",
13755ffb0c9Toomas Soome                   rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
13765ffb0c9Toomas Soome            return(mStatus_AlreadyRegistered);
13775ffb0c9Toomas Soome        }
13785ffb0c9Toomas Soome    }
13795ffb0c9Toomas Soome    else
13805ffb0c9Toomas Soome    {
13815ffb0c9Toomas Soome        while (*p && *p != rr) p=&(*p)->next;
13825ffb0c9Toomas Soome        if (*p)
13835ffb0c9Toomas Soome        {
13845ffb0c9Toomas Soome            LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the list",
13855ffb0c9Toomas Soome                   rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
13865ffb0c9Toomas Soome            return(mStatus_AlreadyRegistered);
13875ffb0c9Toomas Soome        }
13885ffb0c9Toomas Soome    }
13895ffb0c9Toomas Soome
13905ffb0c9Toomas Soome    while (*d && *d != rr) d=&(*d)->next;
13915ffb0c9Toomas Soome    if (*d)
13925ffb0c9Toomas Soome    {
13935ffb0c9Toomas Soome        LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the Duplicate list",
13945ffb0c9Toomas Soome               rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
13955ffb0c9Toomas Soome        return(mStatus_AlreadyRegistered);
13965ffb0c9Toomas Soome    }
13975ffb0c9Toomas Soome
13985ffb0c9Toomas Soome    if (rr->DependentOn)
13995ffb0c9Toomas Soome    {
14005ffb0c9Toomas Soome        if (rr->resrec.RecordType == kDNSRecordTypeUnique)
14015ffb0c9Toomas Soome            rr->resrec.RecordType =  kDNSRecordTypeVerified;
1402c65ebfcToomas Soome        else if (rr->resrec.RecordType != kDNSRecordTypeKnownUnique)
14035ffb0c9Toomas Soome        {
1404c65ebfcToomas Soome            LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique or kDNSRecordTypeKnownUnique",
14055ffb0c9Toomas Soome                   rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
14065ffb0c9Toomas Soome            return(mStatus_Invalid);
14075ffb0c9Toomas Soome        }
14085ffb0c9Toomas Soome        if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique)))
14095ffb0c9Toomas Soome        {
14105ffb0c9Toomas Soome            LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
14115ffb0c9Toomas Soome                   rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
14125ffb0c9Toomas Soome            return(mStatus_Invalid);
14135ffb0c9Toomas Soome        }
14145ffb0c9Toomas Soome    }
14155ffb0c9Toomas Soome
14165ffb0c9Toomas Soome    rr->next = mDNSNULL;
14175ffb0c9Toomas Soome
14185ffb0c9Toomas Soome    // Field Group 1: The actual information pertaining to this resource record
14195ffb0c9Toomas Soome    // Set up by client prior to call
14205ffb0c9Toomas Soome
14215ffb0c9Toomas Soome    // Field Group 2: Persistent metadata for Authoritative Records
1422c65ebfcToomas Soome//  rr->Additional1       = set to mDNSNULL  in mDNS_SetupResourceRecord; may be overridden by client
1423c65ebfcToomas Soome//  rr->Additional2       = set to mDNSNULL  in mDNS_SetupResourceRecord; may be overridden by client
1424c65ebfcToomas Soome//  rr->DependentOn       = set to mDNSNULL  in mDNS_SetupResourceRecord; may be overridden by client
1425c65ebfcToomas Soome//  rr->RRSet             = set to mDNSNULL  in mDNS_SetupResourceRecord; may be overridden by client
1426c65ebfcToomas Soome//  rr->Callback          = already set      in mDNS_SetupResourceRecord
1427c65ebfcToomas Soome//  rr->Context           = already set      in mDNS_SetupResourceRecord
1428c65ebfcToomas Soome//  rr->RecordType        = already set      in mDNS_SetupResourceRecord
1429c65ebfcToomas Soome//  rr->HostTarget        = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
1430c65ebfcToomas Soome//  rr->AllowRemoteQuery  = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
14315ffb0c9Toomas Soome    // Make sure target is not uninitialized data, or we may crash writing debugging log messages
14325ffb0c9Toomas Soome    if (rr->AutoTarget && target) target->c[0] = 0;
14335ffb0c9Toomas Soome
14345ffb0c9Toomas Soome    // Field Group 3: Transient state for Authoritative Records
14355ffb0c9Toomas Soome    rr->Acknowledged      = mDNSfalse;
14365ffb0c9Toomas Soome    rr->ProbeCount        = DefaultProbeCountForRecordType(rr->resrec.RecordType);
14375ffb0c9Toomas Soome    rr->ProbeRestartCount = 0;
14385ffb0c9Toomas Soome    rr->AnnounceCount     = InitialAnnounceCount;
14395ffb0c9Toomas Soome    rr->RequireGoodbye    = mDNSfalse;
14405ffb0c9Toomas Soome    rr->AnsweredLocalQ    = mDNSfalse;
14415ffb0c9Toomas Soome    rr->IncludeInProbe    = mDNSfalse;
14425ffb0c9Toomas Soome    rr->ImmedUnicast      = mDNSfalse;
14435ffb0c9Toomas Soome    rr->SendNSECNow       = mDNSNULL;
14445ffb0c9Toomas Soome    rr->ImmedAnswer       = mDNSNULL;
14455ffb0c9Toomas Soome    rr->ImmedAdditional   = mDNSNULL;
14465ffb0c9Toomas Soome    rr->SendRNow          = mDNSNULL;
14475ffb0c9Toomas Soome    rr->v4Requester       = zerov4Addr;
14485ffb0c9Toomas Soome    rr->v6Requester       = zerov6Addr;
14495ffb0c9Toomas Soome    rr->NextResponse      = mDNSNULL;
14505ffb0c9Toomas Soome    rr->NR_AnswerTo       = mDNSNULL;
14515ffb0c9Toomas Soome    rr->NR_AdditionalTo   = mDNSNULL;
14525ffb0c9Toomas Soome    if (!rr->AutoTarget) InitializeLastAPTime(m, rr);
1453c65ebfcToomas Soome//  rr->LastAPTime        = Set for us in InitializeLastAPTime()
1454c65ebfcToomas Soome//  rr->LastMCTime        = Set for us in InitializeLastAPTime()
1455c65ebfcToomas Soome//  rr->LastMCInterface   = Set for us in InitializeLastAPTime()
14565ffb0c9Toomas Soome    rr->NewRData          = mDNSNULL;
14575ffb0c9Toomas Soome    rr->newrdlength       = 0;
14585ffb0c9Toomas Soome    rr->UpdateCallback    = mDNSNULL;
14595ffb0c9Toomas Soome    rr->UpdateCredits     = kMaxUpdateCredits;
14605ffb0c9Toomas Soome    rr->NextUpdateCredit  = 0;
14615ffb0c9Toomas Soome    rr->UpdateBlocked     = 0;
14625ffb0c9Toomas Soome
14635ffb0c9Toomas Soome    // For records we're holding as proxy (except reverse-mapping PTR records) two announcements is sufficient
14645ffb0c9Toomas Soome    if (rr->WakeUp.HMAC.l[0] && !rr->AddressProxy.type) rr->AnnounceCount = 2;
14655ffb0c9Toomas Soome
14665ffb0c9Toomas Soome    // Field Group 4: Transient uDNS state for Authoritative Records
14675ffb0c9Toomas Soome    rr->state             = regState_Zero;
14685ffb0c9Toomas Soome    rr->uselease          = 0;
14695ffb0c9Toomas Soome    rr->expire            = 0;
14705ffb0c9Toomas Soome    rr->Private           = 0;
14715ffb0c9Toomas Soome    rr->updateid          = zeroID;
14725ffb0c9Toomas Soome    rr->updateIntID       = zeroOpaque64;
14735ffb0c9Toomas Soome    rr->zone              = rr->resrec.name;
14745ffb0c9Toomas Soome    rr->nta               = mDNSNULL;
14755ffb0c9Toomas Soome    rr->tcp               = mDNSNULL;
14765ffb0c9Toomas Soome    rr->OrigRData         = 0;
14775ffb0c9Toomas Soome    rr->OrigRDLen         = 0;
14785ffb0c9Toomas Soome    rr->InFlightRData     = 0;
14795ffb0c9Toomas Soome    rr->InFlightRDLen     = 0;
14805ffb0c9Toomas Soome    rr->QueuedRData       = 0;
14815ffb0c9Toomas Soome    rr->QueuedRDLen       = 0;
14825ffb0c9Toomas Soome    //mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo));
14835ffb0c9Toomas Soome    // We should be recording the actual internal port for this service record here. Once we initiate our NAT mapping
14845ffb0c9Toomas Soome    // request we'll subsequently overwrite srv.port with the allocated external NAT port -- potentially multiple
14855ffb0c9Toomas Soome    // times with different values if the external NAT port changes during the lifetime of the service registration.
14865ffb0c9Toomas Soome    //if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port;
1488c65ebfcToomas Soome//  rr->resrec.interface         = already set in mDNS_SetupResourceRecord
1489c65ebfcToomas Soome//  rr->resrec.name->c           = MUST be set by client
1490c65ebfcToomas Soome//  rr->resrec.rrtype            = already set in mDNS_SetupResourceRecord
1491c65ebfcToomas Soome//  rr->resrec.rrclass           = already set in mDNS_SetupResourceRecord
1492c65ebfcToomas Soome//  rr->resrec.rroriginalttl     = already set in mDNS_SetupResourceRecord
1493c65ebfcToomas Soome//  rr->resrec.rdata             = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
14955ffb0c9Toomas Soome    // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
14965ffb0c9Toomas Soome    // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
14975ffb0c9Toomas Soome    // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
14985ffb0c9Toomas Soome    if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; }
15005ffb0c9Toomas Soome    if (rr->AutoTarget)
15015ffb0c9Toomas Soome    {
15025ffb0c9Toomas Soome        SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
15035ffb0c9Toomas Soome#ifndef UNICAST_DISABLED
15045ffb0c9Toomas Soome        // If we have no target record yet, SetTargetToHostName will set rr->state == regState_NoTarget
15055ffb0c9Toomas Soome        // 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.
15065ffb0c9Toomas Soome        if (rr->state == regState_NoTarget)
15075ffb0c9Toomas Soome        {
15085ffb0c9Toomas Soome            // Initialize the target so that we don't crash while logging etc.
15095ffb0c9Toomas Soome            domainname *tar = GetRRDomainNameTarget(&rr->resrec);
15105ffb0c9Toomas Soome            if (tar) tar->c[0] = 0;
15115ffb0c9Toomas Soome            LogInfo("mDNS_Register_internal: record %s in NoTarget state", ARDisplayString(m, rr));
15125ffb0c9Toomas Soome        }
15135ffb0c9Toomas Soome#endif
15145ffb0c9Toomas Soome    }
15155ffb0c9Toomas Soome    else
15165ffb0c9Toomas Soome    {
15175ffb0c9Toomas Soome        rr->resrec.rdlength   = GetRDLength(&rr->resrec, mDNSfalse);
15185ffb0c9Toomas Soome        rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
15195ffb0c9Toomas Soome    }
15205ffb0c9Toomas Soome
15215ffb0c9Toomas Soome    if (!ValidateDomainName(rr->resrec.name))
15225ffb0c9Toomas Soome    { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
15235ffb0c9Toomas Soome
15245ffb0c9Toomas Soome    // Don't do this until *after* we've set rr->resrec.rdlength
15255ffb0c9Toomas Soome    if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata))
15265ffb0c9Toomas Soome    { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
15275ffb0c9Toomas Soome
15285ffb0c9Toomas Soome    rr->resrec.namehash   = DomainNameHashValue(rr->resrec.name);
15295ffb0c9Toomas Soome    rr->resrec.rdatahash  = target ? DomainNameHashValue(target) : RDataHashValue(&rr->resrec);
15305ffb0c9Toomas Soome
15315ffb0c9Toomas Soome    if (RRLocalOnly(rr))
15325ffb0c9Toomas Soome    {
15335ffb0c9Toomas Soome        // If this is supposed to be unique, make sure we don't have any name conflicts.
15345ffb0c9Toomas Soome        // If we found a conflict, we may still want to insert the record in the list but mark it appropriately
15355ffb0c9Toomas Soome        // (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more
15365ffb0c9Toomas Soome        // complications and not clear whether there are any benefits. See rdar:9304275 for details.
15375ffb0c9Toomas Soome        // Hence, just bail out.
1538cda73f6Toomas Soome        // This comment is doesn���t make any sense. -- SC
15395ffb0c9Toomas Soome        if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
15405ffb0c9Toomas Soome        {
15415ffb0c9Toomas Soome            if (CheckAuthRecordConflict(&m->rrauth, rr))
15425ffb0c9Toomas Soome            {
15435ffb0c9Toomas Soome                LogInfo("mDNS_Register_internal: Name conflict %s (%p), InterfaceID %p", ARDisplayString(m, rr), rr, rr->resrec.InterfaceID);
15445ffb0c9Toomas Soome                return mStatus_NameConflict;
15455ffb0c9Toomas Soome            }
15465ffb0c9Toomas Soome        }
15475ffb0c9Toomas Soome    }
15485ffb0c9Toomas Soome
15495ffb0c9Toomas Soome    // For uDNS records, we don't support duplicate checks at this time.
15505ffb0c9Toomas Soome#ifndef UNICAST_DISABLED
15515ffb0c9Toomas Soome    if (AuthRecord_uDNS(rr))
15525ffb0c9Toomas Soome    {
15535ffb0c9Toomas Soome        if (!m->NewLocalRecords) m->NewLocalRecords = rr;
15545ffb0c9Toomas Soome        // When we called SetTargetToHostName, it may have caused mDNS_Register_internal to be re-entered, appending new
15555ffb0c9Toomas Soome        // 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.
15565ffb0c9Toomas Soome        // Note that for AutoTunnel this should never happen, but this check makes the code future-proof.
15575ffb0c9Toomas Soome        while (*p) p=&(*p)->next;
15585ffb0c9Toomas Soome        *p = rr;
15595ffb0c9Toomas Soome        if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
15605ffb0c9Toomas Soome        rr->ProbeCount    = 0;
15615ffb0c9Toomas Soome        rr->ProbeRestartCount = 0;
15625ffb0c9Toomas Soome        rr->AnnounceCount = 0;
15635ffb0c9Toomas Soome        if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr);
15645ffb0c9Toomas Soome        return(mStatus_NoError);            // <--- Note: For unicast records, code currently bails out at this point
15655ffb0c9Toomas Soome    }
15665ffb0c9Toomas Soome#endif
15685ffb0c9Toomas Soome    // Now that we've finished building our new record, make sure it's not identical to one we already have
15695ffb0c9Toomas Soome    if (RRLocalOnly(rr))
15705ffb0c9Toomas Soome    {
15715ffb0c9Toomas Soome        rr->ProbeCount    = 0;
15725ffb0c9Toomas Soome        rr->ProbeRestartCount = 0;
15735ffb0c9Toomas Soome        rr->AnnounceCount = 0;
15745ffb0c9Toomas Soome        r = CheckAuthIdenticalRecord(&m->rrauth, rr);
15755ffb0c9Toomas Soome    }
15765ffb0c9Toomas Soome    else
15775ffb0c9Toomas Soome    {
15785ffb0c9Toomas Soome        for (r = m->ResourceRecords; r; r=r->next)
15795ffb0c9Toomas Soome            if (RecordIsLocalDuplicate(r, rr))
15805ffb0c9Toomas Soome            {
15815ffb0c9Toomas Soome                if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0;
15825ffb0c9Toomas Soome                else break;
15835ffb0c9Toomas Soome            }
15845ffb0c9Toomas Soome    }
15855ffb0c9Toomas Soome
15865ffb0c9Toomas Soome    if (r)
15875ffb0c9Toomas Soome    {
1588c65ebfcToomas Soome        LogInfo("mDNS_Register_internal: Adding to duplicate list %s", ARDisplayString(m,rr));
15895ffb0c9Toomas Soome        *d = rr;
15905ffb0c9Toomas Soome        // If the previous copy of this record is already verified unique,
15915ffb0c9Toomas Soome        // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
15925ffb0c9Toomas Soome        // Setting ProbeCount to zero will cause SendQueries() to advance this record to
15935ffb0c9Toomas Soome        // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
15945ffb0c9Toomas Soome        if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified)
15955ffb0c9Toomas Soome            rr->ProbeCount = 0;
15965ffb0c9Toomas Soome    }
15975ffb0c9Toomas Soome    else
15985ffb0c9Toomas Soome    {
1599c65ebfcToomas Soome        LogInfo("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr));
16005ffb0c9Toomas Soome        if (RRLocalOnly(rr))
16015ffb0c9Toomas Soome        {
16025ffb0c9Toomas Soome            AuthGroup *ag;
16035ffb0c9Toomas Soome            ag = InsertAuthRecord(m, &m->rrauth, rr);
1604cda73f6Toomas Soome            if (ag && !ag->NewLocalOnlyRecords)
1605cda73f6Toomas Soome            {
16065ffb0c9Toomas Soome                m->NewLocalOnlyRecords = mDNStrue;
16075ffb0c9Toomas Soome                ag->NewLocalOnlyRecords = rr;
16085ffb0c9Toomas Soome            }
1609cda73f6Toomas Soome            // No probing for LocalOnly records; acknowledge them right away
16105ffb0c9Toomas Soome            if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
16115ffb0c9Toomas Soome            AcknowledgeRecord(m, rr);
16125ffb0c9Toomas Soome            return(mStatus_NoError);
16135ffb0c9Toomas Soome        }
16145ffb0c9Toomas Soome        else
16155ffb0c9Toomas Soome        {
16165ffb0c9Toomas Soome            if (!m->NewLocalRecords) m->NewLocalRecords = rr;
16175ffb0c9Toomas Soome            *p = rr;
16185ffb0c9Toomas Soome        }
16195ffb0c9Toomas Soome    }
16205ffb0c9Toomas Soome
1621c65ebfcToomas Soome    if (!AuthRecord_uDNS(rr))   // This check is superfluous, given that for unicast records we (currently) bail out above
1622c65ebfcToomas Soome    {
1623c65ebfcToomas Soome        // We have inserted the record in the list. See if we have to advertise the A/AAAA, HINFO, PTR records.
1624c65ebfcToomas Soome        IncrementAutoTargetServices(m, rr);
1625c65ebfcToomas Soome
1626c65ebfcToomas Soome        // For records that are not going to probe, acknowledge them right away
1627c65ebfcToomas Soome        if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
1628c65ebfcToomas Soome            AcknowledgeRecord(m, rr);
1629c65ebfcToomas Soome
1630c65ebfcToomas Soome        // Adding a record may affect whether or not we should sleep
1631c65ebfcToomas Soome        mDNS_UpdateAllowSleep(m);
1632c65ebfcToomas Soome    }
1633c65ebfcToomas Soome
1634cda73f6Toomas Soome    // If this is a non-sleep proxy keepalive record, fetch the MAC address of the remote host.
16355ffb0c9Toomas Soome    // This is used by the in-NIC proxy to send the keepalive packets.
1636cda73f6Toomas Soome    if (!rr->WakeUp.HMAC.l[0] && mDNS_KeepaliveRecord(&rr->resrec))
16375ffb0c9Toomas Soome    {
1638cda73f6Toomas Soome        mDNSAddr raddr;
16395ffb0c9Toomas Soome        // Set the record type to known unique to prevent probing keep alive records.
16405ffb0c9Toomas Soome        // Also make sure we do not announce the keepalive records.
16415ffb0c9Toomas Soome       rr->resrec.RecordType = kDNSRecordTypeKnownUnique;
16425ffb0c9Toomas Soome       rr->AnnounceCount     = 0;
16435ffb0c9Toomas Soome       getKeepaliveRaddr(m, rr, &raddr);
16445ffb0c9Toomas Soome       // This is an asynchronous call. Once the remote MAC address is available, helper will schedule an
16455ffb0c9Toomas Soome       // asynchronous task to update the resource record
1646c65ebfcToomas Soome       mDNSPlatformGetRemoteMacAddr(&raddr);
16475ffb0c9Toomas Soome    }
16485ffb0c9Toomas Soome
16495ffb0c9Toomas Soome    return(mStatus_NoError);
16505ffb0c9Toomas Soome}
16524b22b93rsmDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr)
16535ffb0c9Toomas Soome{
16545ffb0c9Toomas Soome    m->ProbeFailTime = m->timenow;
16555ffb0c9Toomas Soome    m->NumFailedProbes++;
16565ffb0c9Toomas Soome    // If we've had fifteen or more probe failures, rate-limit to one every five seconds.
16575ffb0c9Toomas Soome    // If a bunch of hosts have all been configured with the same name, then they'll all
16585ffb0c9Toomas Soome    // conflict and run through the same series of names: name-2, name-3, name-4, etc.,
16595ffb0c9Toomas Soome    // up to name-10. After that they'll start adding random increments in the range 1-100,
16605ffb0c9Toomas Soome    // so they're more likely to branch out in the available namespace and settle on a set of
16615ffb0c9Toomas Soome    // unique names quickly. If after five more tries the host is still conflicting, then we
16625ffb0c9Toomas Soome    // may have a serious problem, so we start rate-limiting so we don't melt down the network.
16635ffb0c9Toomas Soome    if (m->NumFailedProbes >= 15)
16645ffb0c9Toomas Soome    {
16655ffb0c9Toomas Soome        m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
16665ffb0c9Toomas Soome        LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
16675ffb0c9Toomas Soome               m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
16685ffb0c9Toomas Soome    }
16695ffb0c9Toomas Soome}
16714b22b93rsmDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr)
16725ffb0c9Toomas Soome{
16735ffb0c9Toomas Soome    RData *OldRData = rr->resrec.rdata;
16745ffb0c9Toomas Soome    mDNSu16 OldRDLen = rr->resrec.rdlength;
16755ffb0c9Toomas Soome    SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);    // Update our rdata
16765ffb0c9Toomas Soome    rr->NewRData = mDNSNULL;                                    // Clear the NewRData pointer ...
16775ffb0c9Toomas Soome    if (rr->UpdateCallback)
16785ffb0c9Toomas Soome        rr->UpdateCallback(m, rr, OldRData, OldRDLen);          // ... and let the client know
16795ffb0c9Toomas Soome}
16805ffb0c9Toomas Soome
16815ffb0c9Toomas Soome// Note: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
16824b22b93rs// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
16835ffb0c9Toomas Soome// Exported so uDNS.c can call this
16845ffb0c9Toomas SoomemDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt)
16855ffb0c9Toomas Soome{
16865ffb0c9Toomas Soome    AuthRecord *r2;
16875ffb0c9Toomas Soome    mDNSu8 RecordType = rr->resrec.RecordType;
16885ffb0c9Toomas Soome    AuthRecord **p = &m->ResourceRecords;   // Find this record in our list of active records
16895ffb0c9Toomas Soome    mDNSBool dupList = mDNSfalse;
16905ffb0c9Toomas Soome
16915ffb0c9Toomas Soome    if (RRLocalOnly(rr))
16925ffb0c9Toomas Soome    {
16935ffb0c9Toomas Soome        AuthGroup *a;
16945ffb0c9Toomas Soome        AuthRecord **rp;
16955ffb0c9Toomas Soome
1696c65ebfcToomas Soome        a = AuthGroupForRecord(&m->rrauth, &rr->resrec);
16975ffb0c9Toomas Soome        if (!a) return mDNSfalse;
1698c65ebfcToomas Soome        rp = &a->members;
16995ffb0c9Toomas Soome        while (*rp && *rp != rr) rp=&(*rp)->next;
17005ffb0c9Toomas Soome        p = rp;
17015ffb0c9Toomas Soome    }
17025ffb0c9Toomas Soome    else
17035ffb0c9Toomas Soome    {
17045ffb0c9Toomas Soome        while (*p && *p != rr) p=&(*p)->next;
17055ffb0c9Toomas Soome    }
17065ffb0c9Toomas Soome
17075ffb0c9Toomas Soome    if (*p)
17085ffb0c9Toomas Soome    {
17095ffb0c9Toomas Soome        // We found our record on the main list. See if there are any duplicates that need special handling.
17105ffb0c9Toomas Soome        if (drt == mDNS_Dereg_conflict)     // If this was a conflict, see that all duplicates get the same treatment
17115ffb0c9Toomas Soome        {
17125ffb0c9Toomas Soome            // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
17135ffb0c9Toomas Soome            // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
17145ffb0c9Toomas Soome            for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF;
17155ffb0c9Toomas Soome        }
17165ffb0c9Toomas Soome        else
17175ffb0c9Toomas Soome        {
17185ffb0c9Toomas Soome            // Before we delete the record (and potentially send a goodbye packet)
17195ffb0c9Toomas Soome            // first see if we have a record on the duplicate list ready to take over from it.
17205ffb0c9Toomas Soome            AuthRecord **d = &m->DuplicateRecords;
17215ffb0c9Toomas Soome            while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next;
17225ffb0c9Toomas Soome            if (*d)
17235ffb0c9Toomas Soome            {
17245ffb0c9Toomas Soome                AuthRecord *dup = *d;
17255ffb0c9Toomas Soome                debugf("mDNS_Register_internal: Duplicate record %p taking over from %p %##s (%s)",
17265ffb0c9Toomas Soome                       dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
17275ffb0c9Toomas Soome                *d        = dup->next;      // Cut replacement record from DuplicateRecords list
17285ffb0c9Toomas Soome                if (RRLocalOnly(rr))
17295ffb0c9Toomas Soome                {
17305ffb0c9Toomas Soome                    dup->next = mDNSNULL;
17315ffb0c9Toomas Soome                    if (!InsertAuthRecord(m, &m->rrauth, dup)) LogMsg("mDNS_Deregister_internal: ERROR!! cannot insert %s", ARDisplayString(m, dup));
17325ffb0c9Toomas Soome                }
17335ffb0c9Toomas Soome                else
17345ffb0c9Toomas Soome                {
17355ffb0c9Toomas Soome                    dup->next = rr->next;       // And then...
17365ffb0c9Toomas Soome                    rr->next  = dup;            // ... splice it in right after the record we're about to delete
17375ffb0c9Toomas Soome                }
17385ffb0c9Toomas Soome                dup->resrec.RecordType        = rr->resrec.RecordType;
17395ffb0c9Toomas Soome                dup->ProbeCount      = rr->ProbeCount;
17405ffb0c9Toomas Soome                dup->ProbeRestartCount = rr->ProbeRestartCount;
17415ffb0c9Toomas Soome                dup->AnnounceCount   = rr->AnnounceCount;
17425ffb0c9Toomas Soome                dup->RequireGoodbye  = rr->RequireGoodbye;
17435ffb0c9Toomas Soome                dup->AnsweredLocalQ  = rr->AnsweredLocalQ;
17445ffb0c9Toomas Soome                dup->ImmedAnswer     = rr->ImmedAnswer;
17455ffb0c9Toomas Soome                dup->ImmedUnicast    = rr->ImmedUnicast;
17465ffb0c9Toomas Soome                dup->ImmedAdditional = rr->ImmedAdditional;
17475ffb0c9Toomas Soome                dup->v4Requester     = rr->v4Requester;
17485ffb0c9Toomas Soome                dup->v6Requester     = rr->v6Requester;
17495ffb0c9Toomas Soome                dup->ThisAPInterval  = rr->ThisAPInterval;
17505ffb0c9Toomas Soome                dup->LastAPTime      = rr->LastAPTime;
17515ffb0c9Toomas Soome                dup->LastMCTime      = rr->LastMCTime;
17525ffb0c9Toomas Soome                dup->LastMCInterface = rr->LastMCInterface;
17535ffb0c9Toomas Soome                dup->Private         = rr->Private;
17545ffb0c9Toomas Soome                dup->state           = rr->state;
17555ffb0c9Toomas Soome                rr->RequireGoodbye = mDNSfalse;
17565ffb0c9Toomas Soome                rr->AnsweredLocalQ = mDNSfalse;
17575ffb0c9Toomas Soome            }
17585ffb0c9Toomas Soome        }
17595ffb0c9Toomas Soome    }
17605ffb0c9Toomas Soome    else
17615ffb0c9Toomas Soome    {
17625ffb0c9Toomas Soome        // We didn't find our record on the main list; try the DuplicateRecords list instead.
17635ffb0c9Toomas Soome        p = &m->DuplicateRecords;
17645ffb0c9Toomas Soome        while (*p && *p != rr) p=&(*p)->next;
17655ffb0c9Toomas Soome        // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
17665ffb0c9Toomas Soome        if (*p)
17675ffb0c9Toomas Soome        {
17685ffb0c9Toomas Soome            // Duplicate records are not used for sending wakeups or goodbyes. Hence, deregister them
17695ffb0c9Toomas Soome            // immediately. When there is a conflict, we deregister all the conflicting duplicate records
17705ffb0c9Toomas Soome            // also that have been marked above in this function. In that case, we come here and if we don't
17715ffb0c9Toomas Soome            // deregister (unilink from the DuplicateRecords list), we will be recursing infinitely. Hence,
17725ffb0c9Toomas Soome            // clear the HMAC which will cause it to deregister. See <rdar://problem/10380988> for
17735ffb0c9Toomas Soome            // details.
17745ffb0c9Toomas Soome            rr->WakeUp.HMAC    = zeroEthAddr;
17755ffb0c9Toomas Soome            rr->RequireGoodbye = mDNSfalse;
17765ffb0c9Toomas Soome            rr->resrec.RecordType = kDNSRecordTypeDeregistering;
17775ffb0c9Toomas Soome            dupList = mDNStrue;
17785ffb0c9Toomas Soome        }
17795ffb0c9Toomas Soome        if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
17805ffb0c9Toomas Soome                       rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
17815ffb0c9Toomas Soome    }
17825ffb0c9Toomas Soome
17835ffb0c9Toomas Soome    if (!*p)
17845ffb0c9Toomas Soome    {
17855ffb0c9Toomas Soome        // No need to log an error message if we already know this is a potentially repeated deregistration
17865ffb0c9Toomas Soome        if (drt != mDNS_Dereg_repeat)
17875ffb0c9Toomas Soome            LogMsg("mDNS_Deregister_internal: Record %p not found in list %s", rr, ARDisplayString(m,rr));
17885ffb0c9Toomas Soome        return(mStatus_BadReferenceErr);
17895ffb0c9Toomas Soome    }
17905ffb0c9Toomas Soome
17915ffb0c9Toomas Soome    // If this is a shared record and we've announced it at least once,
17925ffb0c9Toomas Soome    // we need to retract that announcement before we delete the record
17935ffb0c9Toomas Soome
17945ffb0c9Toomas Soome    // If this is a record (including mDNSInterface_LocalOnly records) for which we've given local-only answers then
17955ffb0c9Toomas Soome    // it's tempting to just do "AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse)" here, but that would not not be safe.
17965ffb0c9Toomas Soome    // The AnswerAllLocalQuestionsWithLocalAuthRecord routine walks the question list invoking client callbacks, using the "m->CurrentQuestion"
17975ffb0c9Toomas Soome    // mechanism to cope with the client callback modifying the question list while that's happening.
17985ffb0c9Toomas Soome    // However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain)
17995ffb0c9Toomas Soome    // which means that the "m->CurrentQuestion" mechanism is already in use to protect that list, so we can't use it twice.
18005ffb0c9Toomas Soome    // More generally, if we invoke callbacks from within a client callback, then those callbacks could deregister other
18015ffb0c9Toomas Soome    // records, thereby invoking yet more callbacks, without limit.
18025ffb0c9Toomas Soome    // The solution is to defer delivering the "Remove" events until mDNS_Execute time, just like we do for sending
18035ffb0c9Toomas Soome    // actual goodbye packets.
18054b22b93rs#ifndef UNICAST_DISABLED
18065ffb0c9Toomas Soome    if (AuthRecord_uDNS(rr))
18075ffb0c9Toomas Soome    {
18085ffb0c9Toomas Soome        if (rr->RequireGoodbye)
18095ffb0c9Toomas Soome        {
18105ffb0c9Toomas Soome            if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
18115ffb0c9Toomas Soome            rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
18125ffb0c9Toomas Soome            m->LocalRemoveEvents     = mDNStrue;
18135ffb0c9Toomas Soome            uDNS_DeregisterRecord(m, rr);
18145ffb0c9Toomas Soome            // At this point unconditionally we bail out
18155ffb0c9Toomas Soome            // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration,
18165ffb0c9Toomas Soome            // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration
18175ffb0c9Toomas Soome            // process and will complete asynchronously. Either way we don't need to do anything more here.
18185ffb0c9Toomas Soome            return(mStatus_NoError);
18195ffb0c9Toomas Soome        }
18205ffb0c9Toomas Soome        // Sometimes the records don't complete proper deregistration i.e., don't wait for a response
18215ffb0c9Toomas Soome        // from the server. In that case, if the records have been part of a group update, clear the
18225ffb0c9Toomas Soome        // state here. Some recors e.g., AutoTunnel gets reused without ever being completely initialized
18235ffb0c9Toomas Soome        rr->updateid = zeroID;
18245ffb0c9Toomas Soome
18255ffb0c9Toomas Soome        // We defer cleaning up NAT state only after sending goodbyes. This is important because
18265ffb0c9Toomas Soome        // RecordRegistrationGotZoneData guards against creating NAT state if clientContext is non-NULL.
18275ffb0c9Toomas Soome        // This happens today when we turn on/off interface where we get multiple network transitions
18285ffb0c9Toomas Soome        // and RestartRecordGetZoneData triggers re-registration of the resource records even though
18295ffb0c9Toomas Soome        // they may be in Registered state which causes NAT information to be setup multiple times. Defering
18305ffb0c9Toomas Soome        // the cleanup here keeps clientContext non-NULL and hence prevents that. Note that cleaning up
18315ffb0c9Toomas Soome        // NAT state here takes care of the case where we did not send goodbyes at all.
18325ffb0c9Toomas Soome        if (rr->NATinfo.clientContext)
18335ffb0c9Toomas Soome        {
18345ffb0c9Toomas Soome            mDNS_StopNATOperation_internal(m, &rr->NATinfo);
18355ffb0c9Toomas Soome            rr->NATinfo.clientContext = mDNSNULL;
18365ffb0c9Toomas Soome        }
18375ffb0c9Toomas Soome        if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; }
18385ffb0c9Toomas Soome        if (rr->tcp) { DisposeTCPConn(rr->tcp);       rr->tcp = mDNSNULL; }
18395ffb0c9Toomas Soome    }
18405ffb0c9Toomas Soome#endif // UNICAST_DISABLED
18415ffb0c9Toomas Soome
18425ffb0c9Toomas Soome    if      (RecordType == kDNSRecordTypeUnregistered)
18435ffb0c9Toomas Soome        LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m, rr));
18445ffb0c9Toomas Soome    else if (RecordType == kDNSRecordTypeDeregistering)
18455ffb0c9Toomas Soome    {
18465ffb0c9Toomas Soome        LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m, rr));
18475ffb0c9Toomas Soome        return(mStatus_BadReferenceErr);
18485ffb0c9Toomas Soome    }
18495ffb0c9Toomas Soome
18505ffb0c9Toomas Soome    // <rdar://problem/7457925> Local-only questions don't get remove events for unique records
18515ffb0c9Toomas Soome    // We may want to consider changing this code so that we generate local-only question "rmv"
18525ffb0c9Toomas Soome    // events (and maybe goodbye packets too) for unique records as well as for shared records
18535ffb0c9Toomas Soome    // Note: If we change the logic for this "if" statement, need to ensure that the code in
18545ffb0c9Toomas Soome    // CompleteDeregistration() sets the appropriate state variables to gaurantee that "else"
18555ffb0c9Toomas Soome    // clause will execute here and the record will be cut from the list.
18565ffb0c9Toomas Soome    if (rr->WakeUp.HMAC.l[0] ||
18575ffb0c9Toomas Soome        (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->AnsweredLocalQ)))
18585ffb0c9Toomas Soome    {
18595ffb0c9Toomas Soome        verbosedebugf("mDNS_Deregister_internal: Starting deregistration for %s", ARDisplayString(m, rr));
18605ffb0c9Toomas Soome        rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
18615ffb0c9Toomas Soome        rr->resrec.rroriginalttl = 0;
18625ffb0c9Toomas Soome        rr->AnnounceCount        = rr->WakeUp.HMAC.l[0] ? WakeupCount : (drt == mDNS_Dereg_rapid) ? 1 : GoodbyeCount;
18635ffb0c9Toomas Soome        rr->ThisAPInterval       = mDNSPlatformOneSecond * 2;
18645ffb0c9Toomas Soome        rr->LastAPTime           = m->timenow - rr->ThisAPInterval;
18655ffb0c9Toomas Soome        m->LocalRemoveEvents     = mDNStrue;
18665ffb0c9Toomas Soome        if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
18675ffb0c9Toomas Soome            m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
18685ffb0c9Toomas Soome    }
18695ffb0c9Toomas Soome    else
18705ffb0c9Toomas Soome    {
18715ffb0c9Toomas Soome        if (!dupList && RRLocalOnly(rr))
18725ffb0c9Toomas Soome        {
18735ffb0c9Toomas Soome            AuthGroup *ag = RemoveAuthRecord(m, &m->rrauth, rr);
18745ffb0c9Toomas Soome            if (ag->NewLocalOnlyRecords == rr) ag->NewLocalOnlyRecords = rr->next;
18755ffb0c9Toomas Soome        }
18765ffb0c9Toomas Soome        else
18775ffb0c9Toomas Soome        {
18785ffb0c9Toomas Soome            *p = rr->next;                  // Cut this record from the list
18795ffb0c9Toomas Soome            if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
18805ffb0c9Toomas Soome            DecrementAutoTargetServices(m, rr);
18815ffb0c9Toomas Soome        }
18825ffb0c9Toomas Soome        // If someone is about to look at this, bump the pointer forward
18835ffb0c9Toomas Soome        if (m->CurrentRecord   == rr) m->CurrentRecord   = rr->next;
18845ffb0c9Toomas Soome        rr->next = mDNSNULL;
18855ffb0c9Toomas Soome
18865ffb0c9Toomas Soome        // Should we generate local remove events here?
18875ffb0c9Toomas Soome        // i.e. something like:
18885ffb0c9Toomas Soome        // if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; }
18895ffb0c9Toomas Soome
18905ffb0c9Toomas Soome        verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m, rr));
18915ffb0c9Toomas Soome        rr->resrec.RecordType = kDNSRecordTypeUnregistered;
18925ffb0c9Toomas Soome
18935ffb0c9Toomas Soome        if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared)
18945ffb0c9Toomas Soome            debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
18955ffb0c9Toomas Soome                   rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
18965ffb0c9Toomas Soome
18975ffb0c9Toomas Soome        // If we have an update queued up which never executed, give the client a chance to free that memory
18985ffb0c9Toomas Soome        if (rr->NewRData) CompleteRDataUpdate(m, rr);   // Update our rdata, clear the NewRData pointer, and return memory to the client
18995ffb0c9Toomas Soome
19005ffb0c9Toomas Soome
19015ffb0c9Toomas Soome        // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
19025ffb0c9Toomas Soome        // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
19035ffb0c9Toomas Soome        // In this case the likely client action to the mStatus_MemFree message is to free the memory,
19045ffb0c9Toomas Soome        // so any attempt to touch rr after this is likely to lead to a crash.
19055ffb0c9Toomas Soome        if (drt != mDNS_Dereg_conflict)
19065ffb0c9Toomas Soome        {
19075ffb0c9Toomas Soome            mDNS_DropLockBeforeCallback();      // Allow client to legally make mDNS API calls from the callback
1908c65ebfcToomas Soome            LogInfo("mDNS_Deregister_internal: callback with mStatus_MemFree for %s", ARDisplayString(m, rr));
19095ffb0c9Toomas Soome            if (rr->RecordCallback)
19105ffb0c9Toomas Soome                rr->RecordCallback(m, rr, mStatus_MemFree);         // MUST NOT touch rr after this
19115ffb0c9Toomas Soome            mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
19125ffb0c9Toomas Soome        }
19135ffb0c9Toomas Soome        else
19145ffb0c9Toomas Soome        {
19155ffb0c9Toomas Soome            RecordProbeFailure(m, rr);
19165ffb0c9Toomas Soome            mDNS_DropLockBeforeCallback();      // Allow client to legally make mDNS API calls from the callback
19175ffb0c9Toomas Soome            if (rr->RecordCallback)
19185ffb0c9Toomas Soome                rr->RecordCallback(m, rr, mStatus_NameConflict);    // MUST NOT touch rr after this
19195ffb0c9Toomas Soome            mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
19205ffb0c9Toomas Soome            // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
19215ffb0c9Toomas Soome            // Note that with all the client callbacks going on, by the time we get here all the
19225ffb0c9Toomas Soome            // records we marked may have been explicitly deregistered by the client anyway.
19235ffb0c9Toomas Soome            r2 = m->DuplicateRecords;
19245ffb0c9Toomas Soome            while (r2)
19255ffb0c9Toomas Soome            {
19265ffb0c9Toomas Soome                if (r2->ProbeCount != 0xFF)
19275ffb0c9Toomas Soome                {
19285ffb0c9Toomas Soome                    r2 = r2->next;
19295ffb0c9Toomas Soome                }
19305ffb0c9Toomas Soome                else
19315ffb0c9Toomas Soome                {
1932c65ebfcToomas Soome#if APPLE_OSX_mDNSResponder
1933c65ebfcToomas Soome                    // See if this record was also registered with any D2D plugins.
1934c65ebfcToomas Soome                    D2D_stop_advertising_record(r2);
1935c65ebfcToomas Soome#endif
19365ffb0c9Toomas Soome                    mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict);
19375ffb0c9Toomas Soome                    // As this is a duplicate record, it will be unlinked from the list
19385ffb0c9Toomas Soome                    // immediately
19395ffb0c9Toomas Soome                    r2 = m->DuplicateRecords;
19405ffb0c9Toomas Soome                }
19415ffb0c9Toomas Soome            }
19425ffb0c9Toomas Soome        }
19435ffb0c9Toomas Soome    }
19445ffb0c9Toomas Soome    mDNS_UpdateAllowSleep(m);
19455ffb0c9Toomas Soome    return(mStatus_NoError);
19465ffb0c9Toomas Soome}
19484b22b93rs// ***************************************************************************
19504b22b93rs#pragma mark -
19514b22b93rs#pragma mark - Packet Sending Functions
19544b22b93rsmDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add)
19555ffb0c9Toomas Soome{
19565ffb0c9Toomas Soome    if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse)
19575ffb0c9Toomas Soome    {
19585ffb0c9Toomas Soome        **nrpp = rr;
19595ffb0c9Toomas Soome        // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
19605ffb0c9Toomas Soome        // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
19615ffb0c9Toomas Soome        // The referenced record will definitely be acceptable (by recursive application of this rule)
19625ffb0c9Toomas Soome        if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo;
19635ffb0c9Toomas Soome        rr->NR_AdditionalTo = add;
19645ffb0c9Toomas Soome        *nrpp = &rr->NextResponse;
19655ffb0c9Toomas Soome    }
19665ffb0c9Toomas Soome    debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
19675ffb0c9Toomas Soome}
1969c65ebfcToomas SoomemDNSlocal void AddRRSetAdditionalsToResponseList(mDNS *const m, AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *additional, const mDNSInterfaceID InterfaceID)
1970c65ebfcToomas Soome{
1971c65ebfcToomas Soome    AuthRecord *rr2;
1972c65ebfcToomas Soome    if (additional->resrec.RecordType & kDNSRecordTypeUniqueMask)
1973c65ebfcToomas Soome    {
1974c65ebfcToomas Soome        for (rr2 = m->ResourceRecords; rr2; rr2 = rr2->next)
1975c65ebfcToomas Soome        {
1976c65ebfcToomas Soome            if ((rr2->resrec.namehash == additional->resrec.namehash) &&
1977c65ebfcToomas Soome                (rr2->resrec.rrtype   == additional->resrec.rrtype) &&
1978c65ebfcToomas Soome                (rr2 != additional) &&
1979c65ebfcToomas Soome                (rr2->resrec.RecordType & kDNSRecordTypeUniqueMask) &&