#include <mach/mach.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#include <sys/types.h>
#include <unistd.h>
#include <paths.h>
#include <fcntl.h>
#include <pwd.h>
#include "DNSServiceDiscoveryRequestServer.h"
#include "DNSServiceDiscoveryReply.h"
#include "mDNSClientAPI.h" // Defines the interface to the client layer above
#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
#include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h
#include "GenLinkedList.h"
#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
#define _UNUSED __attribute__ ((unused))
#define LOCAL_DEFAULT_REG 1 // empty string means register in the local domain
#define DEFAULT_REG_DOMAIN "apple.com." // used if the above flag is turned off
mDNSexport mDNS mDNSStorage;
static mDNS_PlatformSupport PlatformStorage;
#define RR_CACHE_SIZE 64
static CacheRecord rrcachestorage[RR_CACHE_SIZE];
static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
static mach_port_t client_death_port = MACH_PORT_NULL;
static mach_port_t exit_m_port = MACH_PORT_NULL;
static mach_port_t info_m_port = MACH_PORT_NULL;
static mach_port_t server_priv_port = MACH_PORT_NULL;
#define MDNS_MM_TIMEOUT 250
static int restarting_via_mach_init = 0;
typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
struct DNSServiceDomainEnumeration_struct
{
DNSServiceDomainEnumeration *next;
mach_port_t ClientMachPort;
DNSQuestion dom; DNSQuestion def; };
typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
struct DNSServiceBrowserResult_struct
{
DNSServiceBrowserResult *next;
int resultType;
domainname result;
};
typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
typedef struct DNSServiceBrowserQuestion
{
struct DNSServiceBrowserQuestion *next;
DNSQuestion q;
} DNSServiceBrowserQuestion;
struct DNSServiceBrowser_struct
{
DNSServiceBrowser *next;
mach_port_t ClientMachPort;
DNSServiceBrowserQuestion *qlist;
DNSServiceBrowserResult *results;
mDNSs32 lastsuccess;
};
typedef struct DNSServiceResolver_struct DNSServiceResolver;
struct DNSServiceResolver_struct
{
DNSServiceResolver *next;
mach_port_t ClientMachPort;
ServiceInfoQuery q;
ServiceInfo i;
mDNSs32 ReportTime;
};
typedef struct ExtraRecordRef
{
ExtraResourceRecord *localRef; ExtraResourceRecord *globalRef; struct ExtraRecordRef *next;
} ExtraRecordRef;
typedef struct DNSServiceRegistration_struct DNSServiceRegistration;
struct DNSServiceRegistration_struct
{
DNSServiceRegistration *next;
mach_port_t ClientMachPort;
mDNSBool autoname;
mDNSBool autorenameLS;
mDNSBool autorenameGS;
mDNSBool deallocate; domainlabel name;
ExtraRecordRef *ExtraRefList;
ServiceRecordSet *gs; ServiceRecordSet ls; };
static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
static DNSServiceBrowser *DNSServiceBrowserList = NULL;
static DNSServiceResolver *DNSServiceResolverList = NULL;
static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
#if MACOSX_MDNS_MALLOC_DEBUGGING
char _malloc_options[] = "AXZ";
static void validatelists(mDNS *const m)
{
DNSServiceDomainEnumeration *e;
DNSServiceBrowser *b;
DNSServiceResolver *l;
DNSServiceRegistration *r;
AuthRecord *rr;
CacheRecord *cr;
DNSQuestion *q;
mDNSu32 slot;
for (e = DNSServiceDomainEnumerationList; e; e=e->next)
if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0)
LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e, e->ClientMachPort);
for (b = DNSServiceBrowserList; b; b=b->next)
if (b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b, b->ClientMachPort);
for (l = DNSServiceResolverList; l; l=l->next)
if (l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l, l->ClientMachPort);
for (r = DNSServiceRegistrationList; r; r=r->next)
if (r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r, r->ClientMachPort);
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType);
for (rr = m->DuplicateRecords; rr; rr=rr->next)
if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF)
LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType);
for (q = m->Questions; q; q=q->next)
if (q->ThisQInterval == (mDNSs32)~0)
LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q, q->ThisQInterval);
for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
for (cr = mDNSStorage.rrcache_hash[slot]; cr; cr=cr->next)
if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF)
LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->resrec.RecordType);
}
void *mallocL(char *msg, unsigned int size)
{
unsigned long *mem = malloc(size+8);
if (!mem)
{
LogMsg("malloc( %s : %d ) failed", msg, size);
return(NULL);
}
else
{
LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]);
mem[0] = 0xDEAD1234;
mem[1] = size;
memset(&mem[2], 0xFF, size);
validatelists(&mDNSStorage);
return(&mem[2]);
}
}
void freeL(char *msg, void *x)
{
if (!x)
LogMsg("free( %s @ NULL )!", msg);
else
{
unsigned long *mem = ((unsigned long *)x) - 2;
if (mem[0] != 0xDEAD1234)
{ LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
if (mem[1] > 24000)
{ LogMsg("free( %s : %ld @ %p) too big!", msg, mem[1], &mem[2]); return; }
LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
memset(mem, 0xFF, mem[1]+8);
validatelists(&mDNSStorage);
free(mem);
}
}
#endif
mDNSlocal void FreeSRS(ServiceRecordSet *s)
{
while (s->Extras)
{
ExtraResourceRecord *extras = s->Extras;
s->Extras = s->Extras->next;
if (extras->r.resrec.rdata != &extras->r.rdatastorage)
freeL("Extra RData", extras->r.resrec.rdata);
freeL("ExtraResourceRecord", extras);
}
if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage)
freeL("TXT RData", s->RR_TXT.resrec.rdata);
if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes);
}
mDNSlocal void FreeDNSServiceRegistration(ServiceRecordSet *srs)
{
DNSServiceRegistration *x = srs->ServiceContext;
ExtraRecordRef *ref, *fptr;
FreeSRS(srs);
if (srs == x->gs)
{
freeL("DNSServiceRegistration GlobalService", srs);
x->gs = NULL;
}
else x->deallocate = mDNStrue;
if (x->deallocate && !x->gs)
{
ref = x->ExtraRefList;
while (ref)
{ fptr = ref; ref = ref->next; freeL("ExtraRecordRef", fptr); }
freeL("DNSServiceRegistration", x);
}
}
mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
{
DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
DNSServiceBrowser **b = &DNSServiceBrowserList;
DNSServiceResolver **l = &DNSServiceResolverList;
DNSServiceRegistration **r = &DNSServiceRegistrationList;
while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
if (*e)
{
DNSServiceDomainEnumeration *x = *e;
*e = (*e)->next;
if (m && m != x)
LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
mDNS_StopGetDomains(&mDNSStorage, &x->dom);
mDNS_StopGetDomains(&mDNSStorage, &x->def);
freeL("DNSServiceDomainEnumeration", x);
return;
}
while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
if (*b)
{
DNSServiceBrowser *x = *b;
DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist;
*b = (*b)->next;
while (qptr)
{
if (m && m != x)
LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x);
else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, qptr->q.qname.c);
mDNS_StopBrowse(&mDNSStorage, &qptr->q);
freePtr = qptr;
qptr = qptr->next;
freeL("DNSServiceBrowserQuestion", freePtr);
}
while (x->results)
{
DNSServiceBrowserResult *r = x->results;
x->results = x->results->next;
freeL("DNSServiceBrowserResult", r);
}
freeL("DNSServiceBrowser", x);
return;
}
while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
if (*l)
{
DNSServiceResolver *x = *l;
*l = (*l)->next;
if (m && m != x)
LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort, x->i.name.c);
mDNS_StopResolveService(&mDNSStorage, &x->q);
freeL("DNSServiceResolver", x);
return;
}
while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
if (*r)
{
DNSServiceRegistration *x = *r;
*r = (*r)->next;
x->autorenameLS = mDNSfalse;
x->autorenameGS = mDNSfalse;
if (m && m != x)
{
LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls), m, x);
if (x->gs) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs), m, x);
}
else
{
LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls));
if (x->gs) LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs));
}
if (x->gs && mDNS_DeregisterService(&mDNSStorage, x->gs))
{
FreeSRS(x->gs);
x->gs = NULL;
}
if (mDNS_DeregisterService(&mDNSStorage, &x->ls))
FreeDNSServiceRegistration(&x->ls);
return;
}
LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
}
#define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
{
DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
DNSServiceBrowser *b = DNSServiceBrowserList;
DNSServiceResolver *l = DNSServiceResolverList;
DNSServiceRegistration *r = DNSServiceRegistrationList;
DNSServiceBrowserQuestion *qptr;
while (e && e->ClientMachPort != c) e = e->next;
while (b && b->ClientMachPort != c) b = b->next;
while (l && l->ClientMachPort != c) l = l->next;
while (r && r->ClientMachPort != c) r = r->next;
if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg);
else if (b)
{
for (qptr = b->qlist; qptr; qptr = qptr->next)
LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg);
}
else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg);
else if (r)
{
LogMsg("%5d: Registration(%##s) %s%s", c, r->ls.RR_SRV.resrec.name.c, reason, msg);
if (r->gs) LogMsg("%5d: Registration(%##s) %s%s", c, r->gs->RR_SRV.resrec.name.c, reason, msg);
}
else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg);
AbortClient(c, m);
}
mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
{
DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
DNSServiceBrowser *b = DNSServiceBrowserList;
DNSServiceResolver *l = DNSServiceResolverList;
DNSServiceRegistration *r = DNSServiceRegistrationList;
DNSServiceBrowserQuestion *qptr;
while (e && e->ClientMachPort != c) e = e->next;
while (b && b->ClientMachPort != c) b = b->next;
while (l && l->ClientMachPort != c) l = l->next;
while (r && r->ClientMachPort != c) r = r->next;
if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
if (b)
{
for (qptr = b->qlist; qptr; qptr = qptr->next)
LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c);
}
if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c);
if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->ls.RR_SRV.resrec.name.c);
return(e || b || l || r);
}
mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
{
mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
(void)unusedport; (void)size; (void)info; if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
{
const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
AbortClient(deathMessage->not_port, NULL);
mach_port_destroy(mach_task_self(), deathMessage->not_port);
}
}
mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
{
mach_port_t prev;
kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
if (r != KERN_SUCCESS)
AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
}
mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
kern_return_t status;
#pragma unused(m)
char buffer[MAX_ESCAPED_DOMAIN_NAME];
DNSServiceDomainEnumerationReplyResultType rt;
DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c);
if (answer->rrtype != kDNSType_PTR) return;
if (!x) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
if (AddRecord)
{
if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
}
else
{
if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
else return;
}
LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
!AddRecord ? "RemoveDomain" :
question == &x->dom ? "AddDomain" : "AddDomainDefault");
ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
if (status == MACH_SEND_TIMED_OUT)
AbortBlockedClient(x->ClientMachPort, "enumeration", x);
}
mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
int regDom)
{
(void)unusedserver; mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
x->ClientMachPort = client;
x->next = DNSServiceDomainEnumerationList;
DNSServiceDomainEnumerationList = x;
verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
kern_return_t status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT);
if (status == MACH_SEND_TIMED_OUT)
{ AbortBlockedClient(x->ClientMachPort, "local enumeration", x); return(mStatus_UnknownErr); }
err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, FoundDomain, x);
if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, FoundDomain, x);
if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; }
LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c);
EnableDeathNotificationForClient(client, x);
return(mStatus_NoError);
fail:
LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client, regDom, errormsg, err);
return(err);
}
mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
(void)m;
if (answer->rrtype != kDNSType_PTR)
{ LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
domainlabel name;
domainname type, domain;
if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
{
LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
answer->name.c, answer->rdata->u.name.c);
return;
}
DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; }
verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c);
AssignDomainName(x->result, answer->rdata->u.name);
if (AddRecord)
x->resultType = DNSServiceBrowserReplyAddInstance;
else x->resultType = DNSServiceBrowserReplyRemoveInstance;
x->next = NULL;
DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
DNSServiceBrowserResult **p = &browser->results;
while (*p) p = &(*p)->next;
*p = x;
}
mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
DNSCString regtype, DNSCString domain)
{
(void)unusedserver; mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
DNameListElem *SearchDomains = NULL, *sdPtr;
DNSServiceBrowserQuestion *qptr;
if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
domainname t, d;
if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; }
DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
x->ClientMachPort = client;
x->results = NULL;
x->lastsuccess = 0;
x->qlist = NULL;
x->next = DNSServiceBrowserList;
DNSServiceBrowserList = x;
if (!domain[0] && (!strcmp(regtype, "_ichat._tcp.") || !strcmp(regtype, "_presence._tcp.")))
domain = "local.";
if (domain[0])
{
x->qlist = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
x->qlist->next = NULL;
if (!x->qlist) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; }
if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; }
LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, d.c);
err = mDNS_StartBrowse(&mDNSStorage, &x->qlist->q, &t, &d, mDNSInterface_Any, FoundInstance, x);
if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; }
}
else
{
SearchDomains = mDNSPlatformGetSearchDomainList();
if (!SearchDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; }
for (sdPtr = SearchDomains; sdPtr; sdPtr = sdPtr->next)
{
qptr = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion));
if (!qptr) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; }
qptr->next = x->qlist;
x->qlist = qptr;
LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, sdPtr->name.c);
err = mDNS_StartBrowse(&mDNSStorage, &qptr->q, &t, &sdPtr->name, mDNSInterface_Any, FoundInstance, x);
if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; }
}
}
EnableDeathNotificationForClient(client, x);
mDNS_FreeDNameList(SearchDomains);
return(mStatus_NoError);
badparam:
err = mStatus_BadParamErr;
fail:
LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err);
if (SearchDomains) mDNS_FreeDNameList(SearchDomains);
return(err);
}
mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
{
kern_return_t status;
DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID;
if (query->info->InterfaceID == (mDNSInterfaceID)~0) ifx = mDNSNULL;
struct sockaddr_storage interface;
struct sockaddr_storage address;
char cstring[1024];
int i, pstrlen = query->info->TXTinfo[0];
(void)m;
if (query->info->TXTlen > sizeof(cstring)) return;
bzero(&interface, sizeof(interface));
bzero(&address, sizeof(address));
if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
{
struct sockaddr_in *sin = (struct sockaddr_in*)&interface;
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
}
else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_flowinfo = 0;
sin6->sin6_port = 0;
sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6;
sin6->sin6_scope_id = ifx->scope_id;
}
if (query->info->ip.type == mDNSAddrType_IPv4)
{
struct sockaddr_in *sin = (struct sockaddr_in*)&address;
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
sin->sin_port = query->info->port.NotAnInteger;
sin->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
}
else
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = query->info->port.NotAnInteger;
sin6->sin6_flowinfo = 0;
sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6;
sin6->sin6_scope_id = ifx ? ifx->scope_id : 0;
}
for (i=1; i<query->info->TXTlen; i++)
{
if (--pstrlen >= 0)
cstring[i-1] = query->info->TXTinfo[i];
else
{
cstring[i-1] = 1;
pstrlen = query->info->TXTinfo[i];
}
}
cstring[i-1] = 0;
LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort,
x->i.name.c, &query->info->ip, mDNSVal16(query->info->port));
status = DNSServiceResolverReply_rpc(x->ClientMachPort,
(char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
if (status == MACH_SEND_TIMED_OUT)
AbortBlockedClient(x->ClientMachPort, "resolve", x);
}
mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
DNSCString name, DNSCString regtype, DNSCString domain)
{
(void)unusedserver; mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
domainlabel n;
domainname t, d, srv;
if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; }
if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
x->ClientMachPort = client;
x->i.InterfaceID = mDNSInterface_Any;
x->i.name = srv;
x->ReportTime = (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond) | 1;
if (SameDomainLabel(t.c, (mDNSu8*)"\x6_ichat")) x->ReportTime = 0;
x->next = DNSServiceResolverList;
DNSServiceResolverList = x;
LogOperation("%5d: DNSServiceResolver(%##s) START", client, x->i.name.c);
err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }
EnableDeathNotificationForClient(client, x);
return(mStatus_NoError);
badparam:
err = mStatus_BadParamErr;
fail:
LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err);
return(err);
}
mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
{
DNSServiceRegistration *x = (DNSServiceRegistration*)sr->ServiceContext;
if (result == mStatus_NoError)
{
kern_return_t status;
LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr));
status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
if (status == MACH_SEND_TIMED_OUT)
AbortBlockedClient(x->ClientMachPort, "registration success", x);
}
else if (result == mStatus_NameConflict)
{
LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr));
if (x->autoname)
mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
else
{
kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
if (status == MACH_SEND_TIMED_OUT)
AbortBlockedClient(x->ClientMachPort, "registration conflict", x);
}
}
else if (result == mStatus_MemFree)
{
mDNSBool *autorename = (sr == &x->ls ? &x->autorenameLS : &x->autorenameGS);
if (*autorename)
{
debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c);
*autorename = mDNSfalse;
x->name = mDNSStorage.nicelabel;
mDNS_RenameAndReregisterService(m, sr, &x->name);
}
else
{
DNSServiceRegistration **r = &DNSServiceRegistrationList;
while (*r && *r != x) r = &(*r)->next;
if (*r)
{
LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.resrec.name.c);
*r = (*r)->next;
}
LogOperation("%5d: DNSServiceRegistration(%##s, %u) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr));
FreeDNSServiceRegistration(sr);
}
}
else
{
LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld",
x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr), result);
if (sr == x->gs) { freeL("RegCallback - ServiceRecordSet", x->gs); x->gs = NULL; }
}
}
mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port)
{
int count = 1; AuthRecord *rr;
for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
if (rr->resrec.rrtype == kDNSType_SRV &&
rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger &&
SameDomainName(&rr->resrec.name, srv))
count++;
if (count > 1)
LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.",
x->ClientMachPort, count, srv->c, mDNSVal16(port));
}
mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString name, DNSCString regtype, DNSCString domain,
int notAnIntPort, DNSCString txtRecord, DNSServiceRegistration *x)
{
ServiceRecordSet *srs = NULL; mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
AuthRecord *SubTypes = mDNSNULL;
mDNSu32 i, NumSubTypes = 0;
char *comma = regtype;
while (*comma && *comma != ',') comma++;
if (*comma) {
*comma = 0; char *p = comma + 1; while (*p)
{
if ( !(*p && *p != ',')) { errormsg = "Bad Service SubType"; goto badparam; }
while (*p && *p != ',') p++;
if (*p) *p++ = 0;
NumSubTypes++;
}
}
domainlabel n;
domainname t, d;
domainname srv;
if (!name[0]) n = mDNSStorage.nicelabel;
else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; }
if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
mDNSIPPort port;
port.NotAnInteger = notAnIntPort;
unsigned char txtinfo[1024] = "";
unsigned int data_len = 0;
unsigned int size = sizeof(RDataBody);
unsigned char *pstring = &txtinfo[data_len];
char *ptr = txtRecord;
while (*ptr)
{
if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
if (*ptr == 1) {
pstring = &txtinfo[data_len];
pstring[0] = 0;
ptr++;
}
else
{
if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
pstring[++pstring[0]] = *ptr++;
}
}
data_len++;
if (size < data_len)
size = data_len;
if (!x)
{
x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size);
if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
bzero(x, sizeof(*x) - sizeof(RDataBody) + size);
x->ClientMachPort = client;
x->autoname = (!name[0]);
x->name = n;
x->next = DNSServiceRegistrationList;
DNSServiceRegistrationList = x;
srs = &x->ls;
}
else
{
x->gs = mallocL("DNSServiceRegistration GlobalService", sizeof(ServiceRecordSet) - sizeof(RDataBody) + size);
if (!x->gs) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
srs = x->gs;
bzero(srs, sizeof(ServiceRecordSet) - sizeof(RDataBody) + size);
}
if (NumSubTypes)
{
SubTypes = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
if (!SubTypes) { freeL("DNSServiceRegistration", x); err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
for (i = 0; i < NumSubTypes; i++)
{
comma++; MakeDomainNameFromDNSNameString(&SubTypes[i].resrec.name, comma);
while (*comma) comma++; }
}
LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START",
x->ClientMachPort, name, regtype, domain, mDNSVal16(port));
if (port.NotAnInteger) CheckForDuplicateRegistrations(x, &srv, port);
err = mDNS_RegisterService(&mDNSStorage, srs,
&x->name, &t, &d, mDNSNULL, port, txtinfo, data_len, SubTypes, NumSubTypes, mDNSInterface_Any, RegCallback, x);
if (err)
{
if (srs == &x->ls) AbortClient(client, x); else FreeDNSServiceRegistration(x->gs);
errormsg = "mDNS_RegisterService";
goto fail;
}
return x;
badtxt:
LogMsg("%5d: TXT record: %.100s...", client, txtRecord);
badparam:
err = mStatus_BadParamErr;
fail:
LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
client, name, regtype, domain, notAnIntPort, errormsg, err);
return NULL;
}
mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
DNSCString name, DNSCString regtype, DNSCString domain, int notAnIntPort, DNSCString txtRecord)
{
(void)unusedserver; mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
DNSServiceRegistration *x = NULL;
if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; }
x = RegisterService(client, name, regtype, *domain ? domain : "local.", notAnIntPort, txtRecord, NULL);
if (!x) { err = mStatus_UnknownErr; goto fail; }
if (!*domain && mDNSStorage.uDNS_info.ServiceRegDomain[0] && strcmp(regtype, "_presence._tcp.") && strcmp(regtype, "_ichat._tcp."))
x = RegisterService(client, name, regtype, mDNSStorage.uDNS_info.ServiceRegDomain, notAnIntPort, txtRecord, x);
EnableDeathNotificationForClient(client, x);
return(mStatus_NoError);
fail:
LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)",
client, name, regtype, domain, notAnIntPort, errormsg, err);
return mStatus_UnknownErr;
}
mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
{
(void)m; if (result == mStatus_ConfigChanged)
{
DNSServiceRegistration *r;
for (r = DNSServiceRegistrationList; r; r=r->next)
if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c))
{
debugf("NetworkChanged renaming %#s to %#s", r->name.c, mDNSStorage.nicelabel.c);
r->autorenameLS = mDNStrue;
mDNS_DeregisterService(&mDNSStorage, &r->ls);
if (r->gs) { mDNS_DeregisterService(&mDNSStorage, r->gs); r->autorenameGS = mDNStrue; }
}
udsserver_handle_configchange();
}
else if (result == mStatus_GrowCache)
{
mDNSu32 numrecords = m->rrcache_size;
CacheRecord *storage = mallocL("mStatus_GrowCache", sizeof(CacheRecord) * numrecords);
if (storage) mDNS_GrowCache(&mDNSStorage, storage, numrecords);
}
}
mDNSlocal ExtraResourceRecord *AddExtraRecord(DNSServiceRegistration *x, ServiceRecordSet *srs, mach_port_t client,
int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
{
mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
domainname *name = (domainname *)"";
name = &srs->RR_SRV.resrec.name;
(void)x;
unsigned int size = sizeof(RDataBody);
if (size < data_len)
size = data_len;
ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
extra->r.resrec.rrtype = type;
extra->r.rdatastorage.MaxRDLength = size;
extra->r.resrec.rdlength = data_len;
memcpy(&extra->r.rdatastorage.u.data, data, data_len);
LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
client, srs->RR_SRV.resrec.name.c, type, data_len, extra);
err = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl);
if (err)
{
freeL("Extra Resource Record", extra);
errormsg = "mDNS_AddRecordToService";
goto fail;
}
return extra;
fail:
LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err);
return NULL;
}
mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client,
int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference)
{
(void)unusedserver; mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
DNSServiceRegistration *x = DNSServiceRegistrationList;
while (x && x->ClientMachPort != client) x = x->next;
if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
ExtraRecordRef *ref = mallocL("ExtraRecordRef", sizeof(ExtraRecordRef));
if (!ref) { LogMsg("ERROR: malloc"); return mStatus_NoMemoryErr; }
ref->localRef = AddExtraRecord(x, &x->ls, client, type, data, data_len, ttl);
if (!ref->localRef) { freeL("ExtraRecordRef", ref); *reference = (natural_t)NULL; return mStatus_UnknownErr; }
if (x->gs) ref->globalRef = AddExtraRecord(x, x->gs, client, type, data, data_len, ttl); else ref->globalRef = NULL;
ref->next = x->ExtraRefList;
x->ExtraRefList = ref;
*reference = (natural_t)ref;
return mStatus_NoError;
fail:
LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, x->name.c, type, data_len, errormsg, err);
return mStatus_UnknownErr;
}
mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData)
{
(void)m; if (OldRData != &rr->rdatastorage)
freeL("Old RData", OldRData);
}
mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
{
mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
domainname *name = (domainname *)"";
name = &srs->RR_SRV.resrec.name;
unsigned int size = sizeof(RDataBody);
if (size < data_len)
size = data_len;
RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
newrdata->MaxRDLength = size;
memcpy(&newrdata->u, data, data_len);
LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)",
client, srs->RR_SRV.resrec.name.c, data_len);
err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
if (err)
{
errormsg = "mDNS_Update";
freeL("RData", newrdata);
return err;
}
return(mStatus_NoError);
fail:
LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%ld)", client, name->c, data_len, errormsg, err);
return(err);
}
mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client,
natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl)
{
mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
domainname *name = (domainname *)"";
AuthRecord *gRR, *lRR;
(void)unusedserver; if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
DNSServiceRegistration *x = DNSServiceRegistrationList;
while (x && x->ClientMachPort != client) x = x->next;
if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
if (!reference)
{
lRR = &x->ls.RR_TXT;
gRR = x->gs ? &x->gs->RR_TXT : NULL;
}
else
{
ExtraRecordRef *ref;
for (ref = x->ExtraRefList; ref; ref= ref->next)
if (ref == (ExtraRecordRef *)reference) break;
if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
lRR = &ref->localRef->r;
gRR = ref->globalRef ? &ref->globalRef->r : NULL;
}
err = UpdateRecord(&x->ls, client, lRR, data, data_len, ttl);
if (err) goto fail;
if (gRR) UpdateRecord(x->gs, client, gRR, data, data_len, ttl); return mStatus_NoError;
fail:
LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err);
return(err);
}
mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client)
{
domainname *name = &srs->RR_SRV.resrec.name;
mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name.c);
err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra);
if (err) { errormsg = "mDNS_RemoveRecordFromService (No such record)"; goto fail; }
if (extra->r.resrec.rdata != &extra->r.rdatastorage)
freeL("Extra RData", extra->r.resrec.rdata);
freeL("ExtraResourceRecord", extra);
return(mStatus_NoError);
fail:
LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s", client, name->c, errormsg, err);
return(err);
}
mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
natural_t reference)
{
(void)unusedserver; mStatus err = mStatus_NoError;
const char *errormsg = "Unknown";
ExtraRecordRef *ref, *prev = NULL;
if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; }
DNSServiceRegistration *x = DNSServiceRegistrationList;
while (x && x->ClientMachPort != client) x = x->next;
if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
ref = x->ExtraRefList;
while (ref)
{
if (ref == (ExtraRecordRef *)ref) break;
prev = ref;
ref = ref->next;
}
if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; }
err = RemoveRecord(&x->ls, ref->localRef, client);
if (x->gs && ref->globalRef) RemoveRecord(x->gs, ref->globalRef, client);
if (prev) prev->next = ref->next;
else x->ExtraRefList = ref->next;
ref->next = NULL;
freeL("ExtraRecordRef", ref);
return err;
fail:
LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%ld)", client, reference, errormsg, err);
return(err);
}
mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
mig_reply_error_t *request = msg;
mig_reply_error_t *reply;
mach_msg_return_t mr;
int options;
(void)port; (void)size; (void)info;
reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
(void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
{
if (reply->RetCode == MIG_NO_REPLY)
{
CFAllocatorDeallocate(NULL, reply);
return;
}
request->Head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&request->Head);
}
if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
{
if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
mach_msg_destroy(&reply->Head);
CFAllocatorDeallocate(NULL, reply);
return;
}
options = MACH_SEND_MSG;
if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
options |= MACH_SEND_TIMEOUT;
mr = mach_msg(&reply->Head,
options,
reply->Head.msgh_size,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
switch (mr)
{
case MACH_SEND_INVALID_DEST:
case MACH_SEND_TIMED_OUT:
mach_msg_destroy(&reply->Head);
break;
default :
break;
}
CFAllocatorDeallocate(NULL, reply);
}
mDNSlocal kern_return_t registerBootstrapService()
{
kern_return_t status;
mach_port_t service_send_port, service_rcv_port;
debugf("Registering Bootstrap Service");
status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
if (status == KERN_SUCCESS)
{
server_priv_port = bootstrap_port;
restarting_via_mach_init = TRUE;
}
else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
{
status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
FALSE , &server_priv_port);
if (status != KERN_SUCCESS) return status;
status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
if (status != KERN_SUCCESS)
{
mach_port_deallocate(mach_task_self(), server_priv_port);
return status;
}
status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
if (status != KERN_SUCCESS)
{
mach_port_deallocate(mach_task_self(), server_priv_port);
mach_port_deallocate(mach_task_self(), service_send_port);
return status;
}
assert(service_send_port == service_rcv_port);
}
mach_port_destroy(mach_task_self(), service_rcv_port);
return status;
}
mDNSlocal kern_return_t destroyBootstrapService()
{
debugf("Destroying Bootstrap Service");
return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
}
mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
(void)port; (void)msg; (void)size; (void)info;
LogMsgIdent(mDNSResponderVersionString, "stopping");
debugf("ExitCallback: destroyBootstrapService");
if (!mDNS_DebugMode)
destroyBootstrapService();
debugf("ExitCallback: Aborting MIG clients");
while (DNSServiceDomainEnumerationList)
AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
while (DNSServiceBrowserList)
AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList);
while (DNSServiceResolverList)
AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList);
while (DNSServiceRegistrationList)
AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList);
debugf("ExitCallback: mDNS_Close");
mDNS_Close(&mDNSStorage);
if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
exit(0);
}
mDNSlocal void HandleSIGTERM(int signal)
{
(void)signal; debugf(" ");
debugf("SIGINT/SIGTERM");
mach_msg_header_t header;
header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
header.msgh_remote_port = exit_m_port;
header.msgh_local_port = MACH_PORT_NULL;
header.msgh_size = sizeof(header);
header.msgh_id = 0;
if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
{ LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); }
}
mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
(void)port; (void)msg; (void)size; (void)info; DNSServiceDomainEnumeration *e;
DNSServiceBrowser *b;
DNSServiceResolver *l;
DNSServiceRegistration *r;
NetworkInterfaceInfoOSX *i;
mDNSu32 slot;
CacheRecord *rr;
mDNSu32 CacheUsed = 0, CacheActive = 0;
mDNSs32 now = mDNSPlatformTimeNow();
LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----");
for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
{
mDNSu32 SlotUsed = 0;
for (rr = mDNSStorage.rrcache_hash[slot]; rr; rr=rr->next)
{
mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond;
CacheUsed++;
SlotUsed++;
if (rr->CRActiveQuestion) CacheActive++;
LogMsgNoIdent("%s%6ld %-6s%-6s%s", rr->CRActiveQuestion ? "*" : " ", remain, DNSTypeName(rr->resrec.rrtype),
((NetworkInterfaceInfoOSX *)rr->resrec.InterfaceID)->ifa_name, GetRRDisplayString(&mDNSStorage, rr));
usleep(1000); }
if (mDNSStorage.rrcache_used[slot] != SlotUsed)
LogMsgNoIdent("Cache use mismatch: rrcache_used[slot] is %lu, true count %lu", mDNSStorage.rrcache_used[slot], SlotUsed);
}
if (mDNSStorage.rrcache_totalused != CacheUsed)
LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed);
if (mDNSStorage.rrcache_active != CacheActive)
LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage.rrcache_active, CacheActive);
LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive);
for (e = DNSServiceDomainEnumerationList; e; e=e->next)
LogMsgNoIdent("%5d: DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c);
for (b = DNSServiceBrowserList; b; b=b->next)
{
DNSServiceBrowserQuestion *qptr;
for (qptr = b->qlist; qptr; qptr = qptr->next)
LogMsgNoIdent("%5d: ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c);
}
for (l = DNSServiceResolverList; l; l=l->next)
LogMsgNoIdent("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c);
for (r = DNSServiceRegistrationList; r; r=r->next)
{
LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->ls.RR_SRV.resrec.name.c, mDNSVal16(r->ls.RR_SRV.resrec.rdata->u.srv.port));
if (r->gs) LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->gs->RR_SRV.resrec.name.c, mDNSVal16(r->gs->RR_SRV.resrec.rdata->u.srv.port));
}
udsserver_info();
for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
{
if (!i->Exists)
LogMsgNoIdent("Interface: %s %5s(%lu) DORMANT",
i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id);
else
LogMsgNoIdent("Interface: %s %5s(%lu) %s %s %2d %s %2d InterfaceID %p %s %s %#a",
i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id,
i->ifinfo.InterfaceActive ? "Active" : " ",
i->ifinfo.IPv4Available ? "v4" : " ", i->ss.sktv4,
i->ifinfo.IPv6Available ? "v6" : " ", i->ss.sktv6,
i->ifinfo.InterfaceID,
i->ifinfo.Advertise ? "Adv" : " ",
i->ifinfo.McastTxRx ? "TxRx" : " ",
&i->ifinfo.ip);
}
LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----");
}
mDNSlocal void HandleSIGINFO(int signal)
{
(void)signal; mach_msg_header_t header;
header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
header.msgh_remote_port = info_m_port;
header.msgh_local_port = MACH_PORT_NULL;
header.msgh_size = sizeof(header);
header.msgh_id = 0;
if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated.");
}
mDNSlocal kern_return_t mDNSDaemonInitialize(void)
{
mStatus err;
CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL);
CFMachPortRef i_port = CFMachPortCreate(NULL, INFOCallback, NULL, NULL);
mach_port_t m_port = CFMachPortGetPort(s_port);
char *MachServerName = mDNSMacOSXSystemBuildNumber(NULL) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0);
CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0);
CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0);
if (status)
{
if (status == 1103)
LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running");
else
LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status), status);
return(status);
}
err = mDNS_Init(&mDNSStorage, &PlatformStorage,
rrcachestorage, RR_CACHE_SIZE,
mDNS_Init_AdvertiseLocalAddresses,
mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
if (err) { LogMsg("Daemon start: mDNS_Init failed %ld", err); return(err); }
client_death_port = CFMachPortGetPort(d_port);
exit_m_port = CFMachPortGetPort(e_port);
info_m_port = CFMachPortGetPort(i_port);
CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode);
CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode);
CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode);
CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls, kCFRunLoopDefaultMode);
CFRelease(d_rls);
CFRelease(s_rls);
CFRelease(e_rls);
CFRelease(i_rls);
if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port);
err = udsserver_init(&mDNSStorage);
if (err) { LogMsg("Daemon start: udsserver_init failed"); return err; }
return(err);
}
mDNSlocal mDNSs32 mDNSDaemonIdle(void)
{
mDNSs32 nextevent = mDNS_Execute(&mDNSStorage);
mDNSs32 now = mDNSPlatformTimeNow();
DNSServiceBrowser *b = DNSServiceBrowserList;
while (b)
{
DNSServiceBrowser *x = b;
b = b->next;
if (x->results) {
while (x->results)
{
DNSServiceBrowserResult *const r = x->results;
domainlabel name;
domainname type, domain;
DeconstructServiceName(&r->result, &name, &type, &domain); char cname[MAX_DOMAIN_LABEL+1]; char ctype[MAX_ESCAPED_DOMAIN_NAME];
char cdom [MAX_ESCAPED_DOMAIN_NAME];
ConvertDomainLabelToCString_unescaped(&name, cname);
ConvertDomainNameToCString(&type, ctype);
ConvertDomainNameToCString(&domain, cdom);
DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1);
if (status == MACH_SEND_TIMED_OUT)
{
if (nextevent - now > mDNSPlatformOneSecond)
nextevent = now + mDNSPlatformOneSecond;
break;
}
else
{
x->lastsuccess = now;
x->results = x->results->next;
freeL("DNSServiceBrowserResult", r);
}
}
if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
AbortBlockedClient(x->ClientMachPort, "browse", x);
}
}
DNSServiceResolver *l;
for (l = DNSServiceResolverList; l; l=l->next)
if (l->ReportTime && now - l->ReportTime >= 0)
{
l->ReportTime = 0;
LogMsg("%5d: DNSServiceResolver(%##s) active for over two minutes. "
"This places considerable burden on the network.", l->ClientMachPort, l->i.name.c);
}
return(nextevent);
}
mDNSexport int main(int argc, char **argv)
{
int i;
kern_return_t status;
for (i=1; i<argc; i++)
{
if (!strcmp(argv[i], "-d")) mDNS_DebugMode = mDNStrue;
}
signal(SIGINT, HandleSIGTERM); signal(SIGTERM, HandleSIGTERM);
signal(SIGINFO, HandleSIGINFO);
if (!mDNS_DebugMode) registerBootstrapService();
if (!mDNS_DebugMode && !restarting_via_mach_init)
exit(0);
if (!mDNS_DebugMode)
{
int fd = open(_PATH_DEVNULL, O_RDWR, 0);
if (fd != -1)
{
if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO);
if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO);
if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO);
if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
(void)close (fd);
}
}
LogMsgIdent(mDNSResponderVersionString, "starting");
status = mDNSDaemonInitialize();
const struct passwd *pw = getpwnam("nobody");
if (pw != NULL)
setuid(pw->pw_uid);
else
setuid(-2);
if (status == 0)
{
int numevents = 0;
int RunLoopStatus = kCFRunLoopRunTimedOut;
while (RunLoopStatus == kCFRunLoopRunTimedOut)
{
mDNSs32 nextevent = mDNSDaemonIdle();
nextevent = udsserver_idle(nextevent);
mDNSs32 ticks = nextevent - mDNSPlatformTimeNow();
static mDNSs32 RepeatedBusy = 0; if (ticks > 1)
RepeatedBusy = 0;
else
{
ticks = 1;
if (++RepeatedBusy >= mDNSPlatformOneSecond * 10)
{ LogMsg("Task Scheduling Error: Continuously busy for the last ten seconds"); RepeatedBusy = 0; }
}
CFAbsoluteTime interval = (CFAbsoluteTime)ticks / (CFAbsoluteTime)mDNSPlatformOneSecond;
verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents, ticks);
numevents = 0;
RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, true);
while (RunLoopStatus == kCFRunLoopRunHandledSource)
{
numevents++;
RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
}
}
LogMsg("ERROR: CFRunLoopRun Exiting.");
mDNS_Close(&mDNSStorage);
}
if (!mDNS_DebugMode) destroyBootstrapService();
return(status);
}
struct CFSocketEventSource
{
udsEventCallback Callback;
void *Context;
int fd;
struct CFSocketEventSource *Next;
CFSocketRef SocketRef;
CFRunLoopSourceRef RLS;
};
typedef struct CFSocketEventSource CFSocketEventSource;
static GenLinkedList gEventSources;
static void cf_callback(CFSocketRef s _UNUSED, CFSocketCallBackType t _UNUSED, CFDataRef dr _UNUSED, const void *c _UNUSED, void *i)
{
CFSocketEventSource *source = (CFSocketEventSource*) i;
source->Callback(source->Context);
}
mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context)
{
CFSocketEventSource *newSource;
CFSocketContext cfContext = { 0, NULL, NULL, NULL, NULL };
if (gEventSources.LinkOffset == 0)
InitLinkedList(&gEventSources, offsetof(CFSocketEventSource, Next));
if (fd >= FD_SETSIZE || fd < 0)
return mStatus_UnsupportedErr;
if (callback == NULL)
return mStatus_BadParamErr;
newSource = (CFSocketEventSource*) calloc(1, sizeof *newSource);
if (NULL == newSource)
return mStatus_NoMemoryErr;
newSource->Callback = callback;
newSource->Context = context;
newSource->fd = fd;
cfContext.info = newSource;
if ( NULL != (newSource->SocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack,
cf_callback, &cfContext)) &&
NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->SocketRef, 0)))
{
CFRunLoopAddSource(CFRunLoopGetCurrent(), newSource->RLS, kCFRunLoopDefaultMode);
AddToTail(&gEventSources, newSource);
}
else
{
if (newSource->SocketRef)
{
CFSocketInvalidate(newSource->SocketRef); CFRelease(newSource->SocketRef);
}
return mStatus_NoMemoryErr;
}
return mStatus_NoError;
}
mStatus udsSupportRemoveFDFromEventLoop(int fd)
{
CFSocketEventSource *iSource;
for (iSource=(CFSocketEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
{
if (fd == iSource->fd)
{
RemoveFromList(&gEventSources, iSource);
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), iSource->RLS, kCFRunLoopDefaultMode);
CFRunLoopSourceInvalidate(iSource->RLS);
CFRelease(iSource->RLS);
CFSocketInvalidate(iSource->SocketRef);
CFRelease(iSource->SocketRef);
free(iSource);
return mStatus_NoError;
}
}
return mStatus_NoSuchNameErr;
}
mDNSexport const char mDNSResponderVersionString[] = STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";