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 strlen() etc. 20 21#include <Events.h> // For WaitNextEvent() 22#include <SIOUX.h> // For SIOUXHandleOneEvent() 23 24#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above 25 26#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform 27 28// These don't have to be globals, but their memory does need to remain valid for as 29// long as the search is going on. They are declared as globals here for simplicity. 30static mDNS m; 31static mDNS_PlatformSupport p; 32static ServiceRecordSet p1, p2; 33static AuthRecord availRec1, availRec2; 34static Boolean availRec2Active; 35 36// This sample code just calls mDNS_RenameAndReregisterService to automatically pick a new 37// unique name for the service. For a device such as a printer, this may be appropriate. 38// For a device with a user interface, and a screen, and a keyboard, the appropriate 39// response may be to prompt the user and ask them to choose a new name for the service. 40mDNSlocal void Callback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) 41{ 42 switch (result) 43 { 44 case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name.c); break; 45 case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name.c); break; 46 case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name.c); break; 47 default: debugf("Callback: %##s Unknown Result %d", sr->RR_SRV.resrec.name.c, result); break; 48 } 49 50 if (result == mStatus_NameConflict) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); 51} 52 53// RegisterService() is a simple wrapper function which takes C string 54// parameters, converts them to domainname parameters, and calls mDNS_RegisterService() 55mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, 56 UInt16 PortAsNumber, const char txtinfo[], 57 const domainlabel *const n, const char type[], const char domain[]) 58{ 59 domainname t; 60 domainname d; 61 char buffer[MAX_ESCAPED_DOMAIN_NAME]; 62 UInt8 txtbuffer[512]; 63 64 MakeDomainNameFromDNSNameString(&t, type); 65 MakeDomainNameFromDNSNameString(&d, domain); 66 67 if (txtinfo) 68 { 69 strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); 70 txtbuffer[0] = (UInt8)strlen(txtinfo); 71 } 72 else 73 txtbuffer[0] = 0; 74 75 mDNS_RegisterService(m, recordset, 76 n, &t, &d, // Name, type, domain 77 mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), 78 txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length 79 mDNSNULL, 0, // Subtypes (none) 80 mDNSInterface_Any, // Interface ID 81 Callback, mDNSNULL, 0); // Callback, context, flags 82 83 ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); 84 printf("Made Service Records for %s\n", buffer); 85} 86 87// RegisterFakeServiceForTesting() simulates the effect of services being registered on 88// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. 89mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordset, const char txtinfo[], 90 const char name[], const char type[], const char domain[]) 91{ 92 static UInt16 NextPort = 0xF000; 93 domainlabel n; 94 MakeDomainLabelFromLiteralString(&n, name); 95 RegisterService(m, recordset, NextPort++, txtinfo, &n, type, domain); 96} 97 98// Done once on startup, and then again every time our address changes 99mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) 100{ 101 char buffer[MAX_ESCAPED_DOMAIN_NAME]; 102 mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; 103 104 ConvertDomainNameToCString(&m->MulticastHostname, buffer); 105 printf("Name %s\n", buffer); 106 printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); 107 108 printf("\n"); 109 printf("Registering Service Records\n"); 110 // Create example printer discovery records 111 //static ServiceRecordSet p1, p2; 112 113 RegisterFakeServiceForTesting(m, &p1, "", "One", "_raop._tcp.", "local."); 114 RegisterFakeServiceForTesting(m, &p2, "", "Two", "_raop._tcp.", "local."); 115 116 return(kOTNoError); 117} 118 119mDNSlocal void AvailCallback(mDNS *const m, AuthRecord *const rr, mStatus result) 120{ 121 Boolean *b = (Boolean *)rr->RecordContext; 122 (void)m; // Unused 123 // Signal that our record is now free for re-use 124 if (result == mStatus_MemFree) *b = false; 125} 126 127mDNSlocal OSStatus mDNSResponderSetAvail(mDNS *m, AuthRecord *rr, ServiceRecordSet *sr) 128{ 129 // 1. Initialize required fields of AuthRecord 130 // 2. Set name of subtype PTR record to our special subtype name denoting "available" instances 131 // 3. Set target of subtype PTR record to point to our SRV record (exactly the same as the main service PTR record) 132 // 4. And register it 133 mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 2*3600, kDNSRecordTypeShared, AvailCallback, &availRec2Active); 134 MakeDomainNameFromDNSNameString(rr->resrec.name, "a._sub._raop._tcp.local."); 135 AssignDomainName(&rr->resrec.rdata->u.name, sr->RR_SRV.resrec.name); 136 return(mDNS_Register(m, rr)); 137} 138 139// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS 140mDNSlocal Boolean YieldSomeTime(UInt32 milliseconds) 141{ 142 extern Boolean SIOUXQuitting; 143 EventRecord e; 144 WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); 145 SIOUXHandleOneEvent(&e); 146 return(SIOUXQuitting); 147} 148 149int main() 150{ 151 mStatus err; 152 Boolean DoneSetup = false; 153 mDNSs32 nextAvail, nextBusy; 154 155 SIOUXSettings.asktosaveonclose = false; 156 SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; 157 158 printf("Multicast DNS Responder\n\n"); 159 printf("This software reports errors using MacsBug breaks,\n"); 160 printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); 161 printf("******************************************************************************\n"); 162 163 err = InitOpenTransport(); 164 if (err) { debugf("InitOpenTransport failed %d", err); return(err); } 165 166 err = mDNS_Init(&m, &p, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, 167 mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); 168 if (err) return(err); 169 170 while (!YieldSomeTime(35)) 171 { 172#if MDNS_ONLYSYSTEMTASK 173 // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. 174 // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() 175 extern void mDNSPlatformIdle(mDNS *const m); 176 mDNSPlatformIdle(&m); // Only needed for debugging version 177#endif 178 if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) 179 { 180 DoneSetup = true; 181 printf("\nListening for mDNS queries...\n"); 182 mDNSResponderTestSetup(&m); 183 mDNSResponderSetAvail(&m, &availRec1, &p1); 184 availRec2Active = false; 185 nextAvail = mDNS_TimeNow(&m) + mDNSPlatformOneSecond * 10; 186 nextBusy = mDNS_TimeNow(&m) + mDNSPlatformOneSecond * 15; 187 } 188 189 if (DoneSetup) 190 { 191 // We check availRec2.RecordType because we don't want to re-register this record 192 // if the previous mDNS_Deregister() has not yet completed 193 if (mDNS_TimeNow(&m) - nextAvail > 0 && !availRec2Active) 194 { 195 printf("Setting Two now available\n"); 196 availRec2Active = true; 197 mDNSResponderSetAvail(&m, &availRec2, &p2); 198 nextAvail = nextBusy + mDNSPlatformOneSecond * 10; 199 } 200 else if (mDNS_TimeNow(&m) - nextBusy > 0) 201 { 202 printf("Setting Two now busy\n"); 203 mDNS_Deregister(&m, &availRec2); 204 nextBusy = nextAvail + mDNSPlatformOneSecond * 5; 205 } 206 } 207 } 208 209 if (p1.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &p1); 210 if (p2.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &p2); 211 if (availRec1.resrec.RecordType) mDNS_Deregister(&m, &availRec1); 212 if (availRec2Active) mDNS_Deregister(&m, &availRec2); 213 214 mDNS_Close(&m); 215 216 return(0); 217} 218