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 <string.h> // For strcpy() 20 21#include <Events.h> // For WaitNextEvent() 22#include <CodeFragments.h> // For SIOkUnresolvedCFragSymbolAddress 23 24#include <SIOUX.h> // For SIOUXHandleOneEvent() 25 26#include <OpenTransport.h> 27#include <OpenTptInternet.h> 28 29#include "dns_sd.h" 30 31#define ns_c_in 1 32#define ns_t_a 1 33 34typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16; 35static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); } 36static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v) 37{ mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } 38 39typedef struct 40{ 41 OTLIFO serviceinfolist; 42 Boolean headerPrinted; 43 Boolean lostRecords; 44} SearcherServices; 45 46typedef struct 47{ 48 SearcherServices *services; 49 char name[kDNSServiceMaxDomainName]; 50 char type[kDNSServiceMaxDomainName]; 51 char domn[kDNSServiceMaxDomainName]; 52 char host[kDNSServiceMaxDomainName]; 53 char text[kDNSServiceMaxDomainName]; 54 InetHost address; 55 mDNSOpaque16 notAnIntPort; 56 DNSServiceRef sdRef; 57 Boolean add; 58 Boolean dom; 59 OTLink link; 60} linkedServiceInfo; 61 62static SearcherServices services; 63 64// PrintServiceInfo prints the service information to standard out 65// A real application might want to do something else with the information 66static void PrintServiceInfo(SearcherServices *services) 67{ 68 OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist)); 69 70 while (link) 71 { 72 linkedServiceInfo *s = OTGetLinkObject(link, linkedServiceInfo, link); 73 74 if (!services->headerPrinted) 75 { 76 printf("%-55s Type Domain Target Host IP Address Port Info\n", "Name"); 77 services->headerPrinted = true; 78 } 79 80 if (s->dom) 81 { 82 if (s->add) printf("%-55s available for browsing\n", s->domn); 83 else printf("%-55s no longer available for browsing\n", s->domn); 84 } 85 else 86 { 87 char ip[16]; 88 unsigned char *p = (unsigned char *)&s->address; 89 sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); 90 printf("%-55s %-16s %-14s ", s->name, s->type, s->domn); 91 if (s->add) printf("%-15s %-15s %5d %s\n", s->host, ip, mDNSVal16(s->notAnIntPort), s->text); 92 else printf("Removed\n"); 93 } 94 95 link = link->fNext; 96 OTFreeMem(s); 97 } 98} 99 100static void FoundInstanceAddress(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, 101 const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) 102{ 103 linkedServiceInfo *info = (linkedServiceInfo *)context; 104 SearcherServices *services = info->services; 105 (void)sdRef; // Unused 106 (void)interfaceIndex; // Unused 107 (void)fullname; // Unused 108 (void)ttl; // Unused 109 if (errorCode == kDNSServiceErr_NoError) 110 if (flags & kDNSServiceFlagsAdd) 111 if (rrclass == ns_c_in && rrtype == ns_t_a && rdlen == sizeof(info->address)) 112 { 113 memcpy(&info->address, rdata, sizeof(info->address)); 114 DNSServiceRefDeallocate(info->sdRef); 115 OTLIFOEnqueue(&services->serviceinfolist, &info->link); 116 } 117} 118 119static void FoundInstanceInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, 120 DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t notAnIntPort, 121 uint16_t txtLen, const unsigned char *txtRecord, void *context) 122{ 123 linkedServiceInfo *info = (linkedServiceInfo *)context; 124 SearcherServices *services = info->services; 125 (void)sdRef; // Unused 126 (void)flags; // Unused 127 (void)interfaceIndex; // Unused 128 (void)errorCode; // Unused 129 (void)fullname; // Unused 130 strcpy(info->host, hosttarget); 131 if (txtLen == 0) info->text[0] = 0; 132 else 133 { 134 strncpy(info->text, (char *)txtRecord+1, txtRecord[0]); 135 info->text[txtRecord[0]] = 0; 136 } 137 info->notAnIntPort.NotAnInteger = notAnIntPort; 138 DNSServiceRefDeallocate(info->sdRef); 139 DNSServiceQueryRecord(&info->sdRef, 0, 0, info->host, ns_t_a, ns_c_in, FoundInstanceAddress, info); 140} 141 142// When a new named instance of a service is found, FoundInstance() is called. 143// In this sample code we turn around and immediately to a DNSServiceResolve() to resolve that service name 144// to find its target host, port, and txtinfo, but a normal browing application would just display the name. 145// Resolving every single thing you find can be quite hard on the network, so you shouldn't do this 146// in a real application. Defer resolving until the client has picked which instance from the 147// long list of services is the one they want to use, and then resolve only that one. 148static void FoundInstance(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, 149 const char *replyName, const char *replyType, const char *replyDomain, void *context) 150{ 151#pragma unused(client, interface, errorCode) 152 SearcherServices *services = (SearcherServices *)context; 153 linkedServiceInfo *info; 154 155 if (!services) { DebugStr("\pFoundInstance: services is NULL"); return; } 156 157 info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); 158 if (!info) { services->lostRecords = true; return; } 159 160 info->services = services; 161 strcpy(info->name, replyName); 162 strcpy(info->type, replyType); 163 strcpy(info->domn, replyDomain); 164 info->text[0] = 0; 165 info->add = (flags & kDNSServiceFlagsAdd) ? true : false; 166 info->dom = false; 167 168 if (!info->add) // If TTL == 0 we're deleting a service, 169 OTLIFOEnqueue(&services->serviceinfolist, &info->link); 170 else // else we're adding a new service 171 DNSServiceResolve(&info->sdRef, 0, 0, info->name, info->type, info->domn, FoundInstanceInfo, info); 172} 173 174// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS 175static Boolean YieldSomeTime(UInt32 milliseconds) 176{ 177 extern Boolean SIOUXQuitting; 178 EventRecord e; 179 WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); 180 SIOUXHandleOneEvent(&e); 181 return(SIOUXQuitting); 182} 183 184int main() 185{ 186 OSStatus err; 187 void *tempmem; 188 DNSServiceRef sdRef; 189 DNSServiceErrorType dse; 190 191 SIOUXSettings.asktosaveonclose = false; 192 SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; 193 SIOUXSettings.rows = 40; 194 SIOUXSettings.columns = 160; 195 196 printf("DNS-SD Search Client\n\n"); 197 printf("This software reports errors using MacsBug breaks,\n"); 198 printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); 199 printf("******************************************************************************\n\n"); 200 201 if (DNSServiceBrowse == (void*)kUnresolvedCFragSymbolAddress) 202 { 203 printf("Before you can use mDNS/DNS-SD clients, you need to place the \n"); 204 printf("\"Multicast DNS & DNS-SD\" Extension in the Extensions Folder and restart\n"); 205 return(-1); 206 } 207 208 err = InitOpenTransport(); 209 if (err) { printf("InitOpenTransport failed %d", err); return(err); } 210 211 // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time 212 tempmem = OTAllocMem(0x10000); 213 if (tempmem) OTFreeMem(tempmem); 214 else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); 215 216 services.serviceinfolist.fHead = NULL; 217 services.headerPrinted = false; 218 services.lostRecords = false; 219 220 printf("Sending mDNS service lookup queries and waiting for responses...\n\n"); 221 dse = DNSServiceBrowse(&sdRef, 0, 0, "_http._tcp", "", FoundInstance, &services); 222 if (dse == kDNSServiceErr_NoError) 223 { 224 while (!YieldSomeTime(35)) 225 { 226 if (services.serviceinfolist.fHead) 227 PrintServiceInfo(&services); 228 229 if (services.lostRecords) 230 { 231 services.lostRecords = false; 232 printf("**** Warning: Out of memory: Records have been missed.\n"); 233 } 234 } 235 } 236 237 DNSServiceRefDeallocate(sdRef); 238 CloseOpenTransport(); 239 return(0); 240} 241