| /* -*- 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 <stdio.h> // For printf() |
| #include <string.h> // For strlen() etc. |
| |
| #include <Events.h> // For WaitNextEvent() |
| #include <SIOUX.h> // 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 |
| |
| // 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. |
| static mDNS m; |
| static mDNS_PlatformSupport p; |
| static ServiceRecordSet p1, p2, afp, http, njp; |
| static AuthRecord browsedomain1, browsedomain2; |
| |
| // This sample code just calls mDNS_RenameAndReregisterService to automatically pick a new |
| // unique name for the service. For a device such as a printer, this may be appropriate. |
| // For a device with a user interface, and a screen, and a keyboard, the appropriate |
| // response may be to prompt the user and ask them to choose a new name for the service. |
| mDNSlocal void Callback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) |
| { |
| switch (result) |
| { |
| case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name->c); break; |
| case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name->c); break; |
| case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name->c); break; |
| default: debugf("Callback: %##s Unknown Result %d", sr->RR_SRV.resrec.name->c, result); break; |
| } |
| |
| if (result == mStatus_NameConflict) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); |
| } |
| |
| // RegisterService() is a simple wrapper function which takes C string |
| // parameters, converts them to domainname parameters, and calls mDNS_RegisterService() |
| mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, |
| UInt16 PortAsNumber, const char txtinfo[], |
| const domainlabel *const n, const char type[], const char domain[]) |
| { |
| domainname t; |
| domainname d; |
| char buffer[MAX_ESCAPED_DOMAIN_NAME]; |
| UInt8 txtbuffer[512]; |
| |
| MakeDomainNameFromDNSNameString(&t, type); |
| MakeDomainNameFromDNSNameString(&d, domain); |
| |
| if (txtinfo) |
| { |
| strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); |
| txtbuffer[0] = (UInt8)strlen(txtinfo); |
| } |
| else |
| txtbuffer[0] = 0; |
| |
| mDNS_RegisterService(m, recordset, |
| n, &t, &d, // Name, type, domain |
| mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), |
| txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length |
| mDNSNULL, 0, // Subtypes (none) |
| mDNSInterface_Any, // Interface ID |
| Callback, mDNSNULL); // Callback and context |
| |
| ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); |
| printf("Made Service Records for %s\n", buffer); |
| } |
| |
| // RegisterFakeServiceForTesting() simulates the effect of services being registered on |
| // dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. |
| mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordset, const char txtinfo[], |
| const char name[], const char type[], const char domain[]) |
| { |
| static UInt16 NextPort = 0xF000; |
| domainlabel n; |
| MakeDomainLabelFromLiteralString(&n, name); |
| RegisterService(m, recordset, NextPort++, txtinfo, &n, type, domain); |
| } |
| |
| // CreateProxyRegistrationForRealService() checks to see if the given port is currently |
| // in use, and if so, advertises the specified service as present on that port. |
| // This is useful for advertising existing real services (Personal Web Sharing, Personal |
| // File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves. |
| mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsNumber, const char txtinfo[], |
| const char *servicetype, ServiceRecordSet *recordset) |
| { |
| InetAddress ia; |
| TBind bindReq; |
| OSStatus err; |
| TEndpointInfo endpointinfo; |
| EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); |
| if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } |
| |
| ia.fAddressType = AF_INET; |
| ia.fPort = mDNSOpaque16fromIntVal(PortAsNumber).NotAnInteger; |
| ia.fHost = 0; |
| bindReq.addr.maxlen = sizeof(ia); |
| bindReq.addr.len = sizeof(ia); |
| bindReq.addr.buf = (UInt8*)&ia; |
| bindReq.qlen = 0; |
| err = OTBind(ep, &bindReq, NULL); |
| |
| if (err == kOTBadAddressErr) |
| RegisterService(m, recordset, PortAsNumber, txtinfo, &m->nicelabel, servicetype, "local."); |
| else if (err) |
| debugf("OTBind failed %d", err); |
| |
| OTCloseProvider(ep); |
| return(noErr); |
| } |
| |
| // Done once on startup, and then again every time our address changes |
| mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) |
| { |
| char buffer[MAX_ESCAPED_DOMAIN_NAME]; |
| mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; |
| |
| ConvertDomainNameToCString(&m->MulticastHostname, buffer); |
| printf("Name %s\n", buffer); |
| printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); |
| |
| printf("\n"); |
| printf("Registering Service Records\n"); |
| // Create example printer discovery records |
| //static ServiceRecordSet p1, p2; |
| |
| #define SRSET 0 |
| #if SRSET==0 |
| RegisterFakeServiceForTesting(m, &p1, "path=/index.html", "Web Server One", "_http._tcp.", "local."); |
| RegisterFakeServiceForTesting(m, &p2, "path=/path.html", "Web Server Two", "_http._tcp.", "local."); |
| #elif SRSET==1 |
| RegisterFakeServiceForTesting(m, &p1, "rn=lpq1", "Epson Stylus 900N", "_printer._tcp.", "local."); |
| RegisterFakeServiceForTesting(m, &p2, "rn=lpq2", "HP LaserJet", "_printer._tcp.", "local."); |
| #else |
| RegisterFakeServiceForTesting(m, &p1, "rn=lpq3", "My Printer", "_printer._tcp.", "local."); |
| RegisterFakeServiceForTesting(m, &p2, "lrn=pq4", "My Other Printer", "_printer._tcp.", "local."); |
| #endif |
| |
| // If AFP Server is running, register a record for it |
| CreateProxyRegistrationForRealService(m, 548, "", "_afpovertcp._tcp.", &afp); |
| |
| // If Web Server is running, register a record for it |
| CreateProxyRegistrationForRealService(m, 80, "", "_http._tcp.", &http); |
| |
| // And pretend we always have an NJP server running on port 80 too |
| //RegisterService(m, &njp, 80, "NJP/", &m->nicelabel, "_njp._tcp.", "local."); |
| |
| // Advertise that apple.com. is available for browsing |
| mDNS_AdvertiseDomains(m, &browsedomain1, mDNS_DomainTypeBrowse, mDNSInterface_Any, "apple.com."); |
| mDNS_AdvertiseDomains(m, &browsedomain2, mDNS_DomainTypeBrowse, mDNSInterface_Any, "IL 2\\4th Floor.apple.com."); |
| |
| return(kOTNoError); |
| } |
| |
| // YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS |
| mDNSlocal 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; |
| |
| SIOUXSettings.asktosaveonclose = false; |
| SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; |
| |
| printf("Multicast DNS Responder\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(&m, &p, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, |
| mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); |
| if (err) return(err); |
| |
| 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(&m); // Only needed for debugging version |
| #endif |
| if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) |
| { |
| DoneSetup = true; |
| printf("\nListening for mDNS queries...\n"); |
| mDNSResponderTestSetup(&m); |
| } |
| } |
| |
| if (p1.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p1); |
| if (p2.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p2); |
| if (afp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &afp); |
| if (http.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &http); |
| if (njp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &njp); |
| |
| mDNS_Close(&m); |
| |
| return(0); |
| } |