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, afp, http, njp; 33static AuthRecord browsedomain1, browsedomain2; 34 35// This sample code just calls mDNS_RenameAndReregisterService to automatically pick a new 36// unique name for the service. For a device such as a printer, this may be appropriate. 37// For a device with a user interface, and a screen, and a keyboard, the appropriate 38// response may be to prompt the user and ask them to choose a new name for the service. 39mDNSlocal void Callback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) 40 { 41 switch (result) 42 { 43 case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name->c); break; 44 case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name->c); break; 45 case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name->c); break; 46 default: debugf("Callback: %##s Unknown Result %d", sr->RR_SRV.resrec.name->c, result); break; 47 } 48 49 if (result == mStatus_NameConflict) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); 50 } 51 52// RegisterService() is a simple wrapper function which takes C string 53// parameters, converts them to domainname parameters, and calls mDNS_RegisterService() 54mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, 55 UInt16 PortAsNumber, const char txtinfo[], 56 const domainlabel *const n, const char type[], const char domain[]) 57 { 58 domainname t; 59 domainname d; 60 char buffer[MAX_ESCAPED_DOMAIN_NAME]; 61 UInt8 txtbuffer[512]; 62 63 MakeDomainNameFromDNSNameString(&t, type); 64 MakeDomainNameFromDNSNameString(&d, domain); 65 66 if (txtinfo) 67 { 68 strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); 69 txtbuffer[0] = (UInt8)strlen(txtinfo); 70 } 71 else 72 txtbuffer[0] = 0; 73 74 mDNS_RegisterService(m, recordset, 75 n, &t, &d, // Name, type, domain 76 mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), 77 txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length 78 mDNSNULL, 0, // Subtypes (none) 79 mDNSInterface_Any, // Interface ID 80 Callback, mDNSNULL); // Callback and context 81 82 ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); 83 printf("Made Service Records for %s\n", buffer); 84 } 85 86// RegisterFakeServiceForTesting() simulates the effect of services being registered on 87// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. 88mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordset, const char txtinfo[], 89 const char name[], const char type[], const char domain[]) 90 { 91 static UInt16 NextPort = 0xF000; 92 domainlabel n; 93 MakeDomainLabelFromLiteralString(&n, name); 94 RegisterService(m, recordset, NextPort++, txtinfo, &n, type, domain); 95 } 96 97// CreateProxyRegistrationForRealService() checks to see if the given port is currently 98// in use, and if so, advertises the specified service as present on that port. 99// This is useful for advertising existing real services (Personal Web Sharing, Personal 100// File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves. 101mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsNumber, const char txtinfo[], 102 const char *servicetype, ServiceRecordSet *recordset) 103 { 104 InetAddress ia; 105 TBind bindReq; 106 OSStatus err; 107 TEndpointInfo endpointinfo; 108 EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); 109 if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } 110 111 ia.fAddressType = AF_INET; 112 ia.fPort = mDNSOpaque16fromIntVal(PortAsNumber).NotAnInteger; 113 ia.fHost = 0; 114 bindReq.addr.maxlen = sizeof(ia); 115 bindReq.addr.len = sizeof(ia); 116 bindReq.addr.buf = (UInt8*)&ia; 117 bindReq.qlen = 0; 118 err = OTBind(ep, &bindReq, NULL); 119 120 if (err == kOTBadAddressErr) 121 RegisterService(m, recordset, PortAsNumber, txtinfo, &m->nicelabel, servicetype, "local."); 122 else if (err) 123 debugf("OTBind failed %d", err); 124 125 OTCloseProvider(ep); 126 return(noErr); 127 } 128 129// Done once on startup, and then again every time our address changes 130mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) 131 { 132 char buffer[MAX_ESCAPED_DOMAIN_NAME]; 133 mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; 134 135 ConvertDomainNameToCString(&m->MulticastHostname, buffer); 136 printf("Name %s\n", buffer); 137 printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); 138 139 printf("\n"); 140 printf("Registering Service Records\n"); 141 // Create example printer discovery records 142 //static ServiceRecordSet p1, p2; 143 144#define SRSET 0 145#if SRSET==0 146 RegisterFakeServiceForTesting(m, &p1, "path=/index.html", "Web Server One", "_http._tcp.", "local."); 147 RegisterFakeServiceForTesting(m, &p2, "path=/path.html", "Web Server Two", "_http._tcp.", "local."); 148#elif SRSET==1 149 RegisterFakeServiceForTesting(m, &p1, "rn=lpq1", "Epson Stylus 900N", "_printer._tcp.", "local."); 150 RegisterFakeServiceForTesting(m, &p2, "rn=lpq2", "HP LaserJet", "_printer._tcp.", "local."); 151#else 152 RegisterFakeServiceForTesting(m, &p1, "rn=lpq3", "My Printer", "_printer._tcp.", "local."); 153 RegisterFakeServiceForTesting(m, &p2, "lrn=pq4", "My Other Printer", "_printer._tcp.", "local."); 154#endif 155 156 // If AFP Server is running, register a record for it 157 CreateProxyRegistrationForRealService(m, 548, "", "_afpovertcp._tcp.", &afp); 158 159 // If Web Server is running, register a record for it 160 CreateProxyRegistrationForRealService(m, 80, "", "_http._tcp.", &http); 161 162 // And pretend we always have an NJP server running on port 80 too 163 //RegisterService(m, &njp, 80, "NJP/", &m->nicelabel, "_njp._tcp.", "local."); 164 165 // Advertise that apple.com. is available for browsing 166 mDNS_AdvertiseDomains(m, &browsedomain1, mDNS_DomainTypeBrowse, mDNSInterface_Any, "apple.com."); 167 mDNS_AdvertiseDomains(m, &browsedomain2, mDNS_DomainTypeBrowse, mDNSInterface_Any, "IL 2\\4th Floor.apple.com."); 168 169 return(kOTNoError); 170 } 171 172// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS 173mDNSlocal Boolean YieldSomeTime(UInt32 milliseconds) 174 { 175 extern Boolean SIOUXQuitting; 176 EventRecord e; 177 WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); 178 SIOUXHandleOneEvent(&e); 179 return(SIOUXQuitting); 180 } 181 182int main() 183 { 184 mStatus err; 185 Boolean DoneSetup = false; 186 187 SIOUXSettings.asktosaveonclose = false; 188 SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; 189 190 printf("Multicast DNS Responder\n\n"); 191 printf("This software reports errors using MacsBug breaks,\n"); 192 printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); 193 printf("******************************************************************************\n"); 194 195 err = InitOpenTransport(); 196 if (err) { debugf("InitOpenTransport failed %d", err); return(err); } 197 198 err = mDNS_Init(&m, &p, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, 199 mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); 200 if (err) return(err); 201 202 while (!YieldSomeTime(35)) 203 { 204#if MDNS_ONLYSYSTEMTASK 205 // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. 206 // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() 207 extern void mDNSPlatformIdle(mDNS *const m); 208 mDNSPlatformIdle(&m); // Only needed for debugging version 209#endif 210 if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) 211 { 212 DoneSetup = true; 213 printf("\nListening for mDNS queries...\n"); 214 mDNSResponderTestSetup(&m); 215 } 216 } 217 218 if (p1.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p1); 219 if (p2.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p2); 220 if (afp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &afp); 221 if (http.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &http); 222 if (njp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &njp); 223 224 mDNS_Close(&m); 225 226 return(0); 227 } 228