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