1/* -*- Mode: C; tab-width: 4 -*- 2 * 3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18#include <stdio.h> // For printf() 19#include <Events.h> // For WaitNextEvent() 20#include <SIOUX.h> // For SIOUXHandleOneEvent() 21 22#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above 23#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform 24 25typedef struct 26 { 27 OTLIFO serviceinfolist; 28 Boolean headerPrinted; 29 Boolean lostRecords; 30 } SearcherServices; 31 32typedef struct { ServiceInfo i; mDNSBool add; mDNSBool dom; OTLink link; } linkedServiceInfo; 33 34// These don't have to be globals, but their memory does need to remain valid for as 35// long as the search is going on. They are declared as globals here for simplicity. 36#define RR_CACHE_SIZE 1000 37static CacheEntity rrcachestorage[RR_CACHE_SIZE]; 38static mDNS mDNSStorage; 39static mDNS_PlatformSupport PlatformSupportStorage; 40static SearcherServices services; 41static DNSQuestion browsequestion, domainquestion; 42 43// PrintServiceInfo prints the service information to standard out 44// A real application might want to do something else with the information 45static void PrintServiceInfo(SearcherServices *services) 46 { 47 OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist)); 48 49 while (link) 50 { 51 linkedServiceInfo *ls = OTGetLinkObject(link, linkedServiceInfo, link); 52 ServiceInfo *s = &ls->i; 53 54 if (!services->headerPrinted) 55 { 56 printf("%-55s Type Domain IP Address Port Info\n", "Name"); 57 services->headerPrinted = true; 58 } 59 60 if (ls->dom) 61 { 62 char c_dom[MAX_ESCAPED_DOMAIN_NAME]; 63 ConvertDomainNameToCString(&s->name, c_dom); 64 if (ls->add) printf("%-55s available for browsing\n", c_dom); 65 else printf("%-55s no longer available for browsing\n", c_dom); 66 } 67 else 68 { 69 domainlabel name; 70 domainname type, domain; 71 char c_name[MAX_DOMAIN_LABEL+1], c_type[MAX_ESCAPED_DOMAIN_NAME], c_dom[MAX_ESCAPED_DOMAIN_NAME], c_ip[20]; 72 DeconstructServiceName(&s->name, &name, &type, &domain); 73 ConvertDomainLabelToCString_unescaped(&name, c_name); 74 ConvertDomainNameToCString(&type, c_type); 75 ConvertDomainNameToCString(&domain, c_dom); 76 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]); 77 78 printf("%-55s %-16s %-14s ", c_name, c_type, c_dom); 79 if (ls->add) printf("%-15s %5d %#s\n", c_ip, mDNSVal16(s->port), s->TXTinfo); 80 else printf("Removed\n"); 81 } 82 83 link = link->fNext; 84 OTFreeMem(ls); 85 } 86 } 87 88// When the name, address, port, and txtinfo for a service is found, FoundInstanceInfo() 89// enqueues a record for PrintServiceInfo() to print. 90// Note, a browsing application would *not* normally need to get all this information -- 91// all it needs is the name, to display to the user. 92// Finding out the address, port, and txtinfo should be deferred to the time that the user 93// actually needs to contact the service to use it. 94static void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) 95 { 96 SearcherServices *services = (SearcherServices *)query->ServiceInfoQueryContext; 97 linkedServiceInfo *info = (linkedServiceInfo *)(query->info); 98 if (query->info->ip.type == mDNSAddrType_IPv4) 99 { 100 mDNS_StopResolveService(m, query); // For this test code, one answer is sufficient 101 OTLIFOEnqueue(&services->serviceinfolist, &info->link); 102 OTFreeMem(query); 103 } 104 } 105 106// When a new named instance of a service is found, FoundInstance() is called. 107// In this sample code we turn around and immediately issue a query to resolve that service name to 108// find its address, port, and txtinfo, but a normal browing application would just display the name. 109static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) 110 { 111 #pragma unused (question) 112 SearcherServices *services = (SearcherServices *)question->QuestionContext; 113 linkedServiceInfo *info; 114 115 debugf("FoundInstance %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); 116 117 if (answer->rrtype != kDNSType_PTR) return; 118 if (!services) { debugf("FoundInstance: services is NULL"); return; } 119 120 info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); 121 if (!info) { services->lostRecords = true; return; } 122 123 info->i.name = answer->rdata->u.name; 124 info->i.InterfaceID = answer->InterfaceID; 125 info->i.ip.type = mDNSAddrType_IPv4; 126 info->i.ip.ip.v4 = zerov4Addr; 127 info->i.port = zeroIPPort; 128 info->add = AddRecord; 129 info->dom = mDNSfalse; 130 131 if (!AddRecord) // If TTL == 0 we're deleting a service, 132 OTLIFOEnqueue(&services->serviceinfolist, &info->link); 133 else // else we're adding a new service 134 { 135 ServiceInfoQuery *q = (ServiceInfoQuery *)OTAllocMem(sizeof(ServiceInfoQuery)); 136 if (!q) { OTFreeMem(info); services->lostRecords = true; return; } 137 mDNS_StartResolveService(m, q, &info->i, FoundInstanceInfo, services); 138 } 139 } 140 141static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) 142 { 143 #pragma unused (m) 144 #pragma unused (question) 145 SearcherServices *services = (SearcherServices *)question->QuestionContext; 146 linkedServiceInfo *info; 147 148 debugf("FoundDomain %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); 149 150 if (answer->rrtype != kDNSType_PTR) return; 151 if (!services) { debugf("FoundDomain: services is NULL"); return; } 152 153 info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); 154 if (!info) { services->lostRecords = true; return; } 155 156 info->i.name = answer->rdata->u.name; 157 info->i.InterfaceID = answer->InterfaceID; 158 info->i.ip.type = mDNSAddrType_IPv4; 159 info->i.ip.ip.v4 = zerov4Addr; 160 info->i.port = zeroIPPort; 161 info->add = AddRecord; 162 info->dom = mDNStrue; 163 164 OTLIFOEnqueue(&services->serviceinfolist, &info->link); 165 } 166 167// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS 168static Boolean YieldSomeTime(UInt32 milliseconds) 169 { 170 extern Boolean SIOUXQuitting; 171 EventRecord e; 172 WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); 173 SIOUXHandleOneEvent(&e); 174 return(SIOUXQuitting); 175 } 176 177int main() 178 { 179 mStatus err; 180 Boolean DoneSetup = false; 181 void *tempmem; 182 183 SIOUXSettings.asktosaveonclose = false; 184 SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; 185 SIOUXSettings.rows = 40; 186 SIOUXSettings.columns = 132; 187 188 printf("Multicast DNS Searcher\n\n"); 189 printf("This software reports errors using MacsBug breaks,\n"); 190 printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); 191 printf("******************************************************************************\n"); 192 193 err = InitOpenTransport(); 194 if (err) { debugf("InitOpenTransport failed %d", err); return(err); } 195 196 err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE, 197 mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); 198 if (err) return(err); 199 200 // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time 201 tempmem = OTAllocMem(0x10000); 202 if (tempmem) OTFreeMem(tempmem); 203 else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); 204 205 services.serviceinfolist.fHead = NULL; 206 services.headerPrinted = false; 207 services.lostRecords = false; 208 209 while (!YieldSomeTime(35)) 210 { 211#if MDNS_ONLYSYSTEMTASK 212 // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. 213 // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() 214 extern void mDNSPlatformIdle(mDNS *const m); 215 mDNSPlatformIdle(&mDNSStorage); // Only needed for debugging version 216#endif 217 if (mDNSStorage.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) 218 { 219 domainname srvtype, srvdom; 220 DoneSetup = true; 221 printf("\nSending mDNS service lookup queries and waiting for responses...\n\n"); 222 MakeDomainNameFromDNSNameString(&srvtype, "_http._tcp."); 223 MakeDomainNameFromDNSNameString(&srvdom, "local."); 224 err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, mDNSfalse, FoundInstance, &services); 225 if (err) break; 226 err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, NULL, mDNSInterface_Any, FoundDomain, &services); 227 if (err) break; 228 } 229 230 if (services.serviceinfolist.fHead) 231 PrintServiceInfo(&services); 232 233 if (services.lostRecords) 234 { 235 services.lostRecords = false; 236 printf("**** Warning: Out of memory: Records have been missed.\n"); 237 } 238 } 239 240 mDNS_StopBrowse(&mDNSStorage, &browsequestion); 241 mDNS_Close(&mDNSStorage); 242 return(0); 243 } 244