/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include // For printf() #include // For WaitNextEvent() #include // For SIOUXHandleOneEvent() #include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform typedef struct { OTLIFO serviceinfolist; Boolean headerPrinted; Boolean lostRecords; } SearcherServices; typedef struct { ServiceInfo i; mDNSBool add; mDNSBool dom; OTLink link; } linkedServiceInfo; // These don't have to be globals, but their memory does need to remain valid for as // long as the search is going on. They are declared as globals here for simplicity. #define RR_CACHE_SIZE 1000 static CacheEntity rrcachestorage[RR_CACHE_SIZE]; static mDNS mDNSStorage; static mDNS_PlatformSupport PlatformSupportStorage; static SearcherServices services; static DNSQuestion browsequestion, domainquestion; // PrintServiceInfo prints the service information to standard out // A real application might want to do something else with the information static void PrintServiceInfo(SearcherServices *services) { OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist)); while (link) { linkedServiceInfo *ls = OTGetLinkObject(link, linkedServiceInfo, link); ServiceInfo *s = &ls->i; if (!services->headerPrinted) { printf("%-55s Type Domain IP Address Port Info\n", "Name"); services->headerPrinted = true; } if (ls->dom) { char c_dom[MAX_ESCAPED_DOMAIN_NAME]; ConvertDomainNameToCString(&s->name, c_dom); if (ls->add) printf("%-55s available for browsing\n", c_dom); else printf("%-55s no longer available for browsing\n", c_dom); } else { domainlabel name; domainname type, domain; char c_name[MAX_DOMAIN_LABEL+1], c_type[MAX_ESCAPED_DOMAIN_NAME], c_dom[MAX_ESCAPED_DOMAIN_NAME], c_ip[20]; DeconstructServiceName(&s->name, &name, &type, &domain); ConvertDomainLabelToCString_unescaped(&name, c_name); ConvertDomainNameToCString(&type, c_type); ConvertDomainNameToCString(&domain, c_dom); sprintf(c_ip, "%d.%d.%d.%d", s->ip.ip.v4.b[0], s->ip.ip.v4.b[1], s->ip.ip.v4.b[2], s->ip.ip.v4.b[3]); printf("%-55s %-16s %-14s ", c_name, c_type, c_dom); if (ls->add) printf("%-15s %5d %#s\n", c_ip, mDNSVal16(s->port), s->TXTinfo); else printf("Removed\n"); } link = link->fNext; OTFreeMem(ls); } } // When the name, address, port, and txtinfo for a service is found, FoundInstanceInfo() // enqueues a record for PrintServiceInfo() to print. // Note, a browsing application would *not* normally need to get all this information -- // all it needs is the name, to display to the user. // Finding out the address, port, and txtinfo should be deferred to the time that the user // actually needs to contact the service to use it. static void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) { SearcherServices *services = (SearcherServices *)query->ServiceInfoQueryContext; linkedServiceInfo *info = (linkedServiceInfo *)(query->info); if (query->info->ip.type == mDNSAddrType_IPv4) { mDNS_StopResolveService(m, query); // For this test code, one answer is sufficient OTLIFOEnqueue(&services->serviceinfolist, &info->link); OTFreeMem(query); } } // When a new named instance of a service is found, FoundInstance() is called. // In this sample code we turn around and immediately issue a query to resolve that service name to // find its address, port, and txtinfo, but a normal browing application would just display the name. static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) { #pragma unused (question) SearcherServices *services = (SearcherServices *)question->QuestionContext; linkedServiceInfo *info; debugf("FoundInstance %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); if (answer->rrtype != kDNSType_PTR) return; if (!services) { debugf("FoundInstance: services is NULL"); return; } info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); if (!info) { services->lostRecords = true; return; } info->i.name = answer->rdata->u.name; info->i.InterfaceID = answer->InterfaceID; info->i.ip.type = mDNSAddrType_IPv4; info->i.ip.ip.v4 = zerov4Addr; info->i.port = zeroIPPort; info->add = AddRecord; info->dom = mDNSfalse; if (!AddRecord) // If TTL == 0 we're deleting a service, OTLIFOEnqueue(&services->serviceinfolist, &info->link); else // else we're adding a new service { ServiceInfoQuery *q = (ServiceInfoQuery *)OTAllocMem(sizeof(ServiceInfoQuery)); if (!q) { OTFreeMem(info); services->lostRecords = true; return; } mDNS_StartResolveService(m, q, &info->i, FoundInstanceInfo, services); } } static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) { #pragma unused (m) #pragma unused (question) SearcherServices *services = (SearcherServices *)question->QuestionContext; linkedServiceInfo *info; debugf("FoundDomain %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); if (answer->rrtype != kDNSType_PTR) return; if (!services) { debugf("FoundDomain: services is NULL"); return; } info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); if (!info) { services->lostRecords = true; return; } info->i.name = answer->rdata->u.name; info->i.InterfaceID = answer->InterfaceID; info->i.ip.type = mDNSAddrType_IPv4; info->i.ip.ip.v4 = zerov4Addr; info->i.port = zeroIPPort; info->add = AddRecord; info->dom = mDNStrue; OTLIFOEnqueue(&services->serviceinfolist, &info->link); } // YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS static Boolean YieldSomeTime(UInt32 milliseconds) { extern Boolean SIOUXQuitting; EventRecord e; WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); SIOUXHandleOneEvent(&e); return(SIOUXQuitting); } int main() { mStatus err; Boolean DoneSetup = false; void *tempmem; SIOUXSettings.asktosaveonclose = false; SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; SIOUXSettings.rows = 40; SIOUXSettings.columns = 132; printf("Multicast DNS Searcher\n\n"); printf("This software reports errors using MacsBug breaks,\n"); printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); printf("******************************************************************************\n"); err = InitOpenTransport(); if (err) { debugf("InitOpenTransport failed %d", err); return(err); } err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE, mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); if (err) return(err); // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time tempmem = OTAllocMem(0x10000); if (tempmem) OTFreeMem(tempmem); else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); services.serviceinfolist.fHead = NULL; services.headerPrinted = false; services.lostRecords = false; while (!YieldSomeTime(35)) { #if MDNS_ONLYSYSTEMTASK // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() extern void mDNSPlatformIdle(mDNS *const m); mDNSPlatformIdle(&mDNSStorage); // Only needed for debugging version #endif if (mDNSStorage.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) { domainname srvtype, srvdom; DoneSetup = true; printf("\nSending mDNS service lookup queries and waiting for responses...\n\n"); MakeDomainNameFromDNSNameString(&srvtype, "_http._tcp."); MakeDomainNameFromDNSNameString(&srvdom, "local."); err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, mDNSfalse, FoundInstance, &services); if (err) break; err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, NULL, mDNSInterface_Any, FoundDomain, &services); if (err) break; } if (services.serviceinfolist.fHead) PrintServiceInfo(&services); if (services.lostRecords) { services.lostRecords = false; printf("**** Warning: Out of memory: Records have been missed.\n"); } } mDNS_StopBrowse(&mDNSStorage, &browsequestion); mDNS_Close(&mDNSStorage); return(0); }