xref: /titanic_50/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java (revision 5ffb0c9b03b5149ff4f5821a62be4a52408ada2a)
1 /* -*- Mode: Java; tab-width: 4 -*-
2  *
3  * Copyright (c) 2004 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 	This file declares and implements DNSSD, the central Java factory class
18 	for doing DNS Service Discovery. It includes the mostly-abstract public
19 	interface, as well as the Apple* implementation subclasses.
20  */
21 
22 
23 package	com.apple.dnssd;
24 
25 
26 /**
27 	DNSSD provides access to DNS Service Discovery features of ZeroConf networking.<P>
28 
29 	It is a factory class that is used to invoke registration and discovery-related
30 	operations. Most operations are non-blocking; clients are called back through an interface
31 	with the result of an operation. Callbacks are made from a separate worker thread.<P>
32 
33 	For example, in this program<P>
34 	<PRE><CODE>
35     class   MyClient implements BrowseListener {
36         void    lookForWebServers() {
37             myBrowser = DNSSD.browse("_http._tcp", this);
38         }
39 
40         public void serviceFound(DNSSDService browser, int flags, int ifIndex,
41                             String serviceName, String regType, String domain) {}
42         ...
43     }</CODE></PRE>
44 	<CODE>MyClient.serviceFound()</CODE> would be called for every HTTP server discovered in the
45 	default browse domain(s).
46 */
47 
48 abstract public class	DNSSD
49 {
50 	/**	Flag indicates to a {@link BrowseListener} that another result is
51 		queued.  Applications should not update their UI to display browse
52 		results if the MORE_COMING flag is set; they will be called at least once
53 		more with the flag clear.
54 	*/
55 	public static final int		MORE_COMING = ( 1 << 0 );
56 
57 	/** If flag is set in a {@link DomainListener} callback, indicates that the result is the default domain. */
58 	public static final int		DEFAULT = ( 1 << 2 );
59 
60     /**	If flag is set, a name conflict will trigger an exception when registering non-shared records.<P>
61     	A name must be explicitly specified when registering a service if this bit is set
62     	(i.e. the default name may not not be used).
63      */
64 	public static final int		NO_AUTO_RENAME = ( 1 << 3 );
65 
66 	/**	If flag is set, allow multiple records with this name on the network (e.g. PTR records)
67 		when registering individual records on a {@link DNSSDRegistration}.
68 	*/
69 	public static final int		SHARED = ( 1 << 4 );
70 
71 	/**	If flag is set, records with this name must be unique on the network (e.g. SRV records). */
72 	public static final int		UNIQUE = ( 1 << 5 );
73 
74 	/** Set flag when calling enumerateDomains() to restrict results to domains recommended for browsing. */
75 	public static final int		BROWSE_DOMAINS = ( 1 << 6 );
76 	/** Set flag when calling enumerateDomains() to restrict results to domains recommended for registration. */
77 	public static final int		REGISTRATION_DOMAINS = ( 1 << 7 );
78 
79 	/** Maximum length, in bytes, of a domain name represented as an escaped C-String. */
80     public static final int     MAX_DOMAIN_NAME = 1009;
81 
82 	/** Pass for ifIndex to specify all available interfaces. */
83     public static final int     ALL_INTERFACES = 0;
84 
85 	/** Pass for ifIndex to specify the localhost interface. */
86     public static final int     LOCALHOST_ONLY = -1;
87 
88 	/** Browse for instances of a service.<P>
89 
90 		Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P>
91 
92 		@param	flags
93 					Currently ignored, reserved for future use.
94 		<P>
95 		@param	ifIndex
96 					If non-zero, specifies the interface on which to browse for services
97 					(the index for a given interface is determined via the if_nametoindex()
98 					family of calls.)  Most applications will pass 0 to browse on all available
99 					interfaces.  Pass -1 to only browse for services provided on the local host.
100 		<P>
101 		@param	regType
102 					The registration type being browsed for followed by the protocol, separated by a
103 					dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
104 		<P>
105 		@param	domain
106 					If non-null, specifies the domain on which to browse for services.
107 					Most applications will not specify a domain, instead browsing on the
108 					default domain(s).
109 		<P>
110 		@param	listener
111 					This object will get called when instances of the service are discovered (or disappear).
112 		<P>
113 		@return		A {@link DNSSDService} that represents the active browse operation.
114 
115 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
116 		@see    RuntimePermission
117 	*/
browse( int flags, int ifIndex, String regType, String domain, BrowseListener listener)118 	public static DNSSDService	browse( int flags, int ifIndex, String regType, String domain, BrowseListener listener)
119 	throws DNSSDException
120 	{ return getInstance()._makeBrowser( flags, ifIndex, regType, domain, listener); }
121 
122 	/** Browse for instances of a service. Use default flags, ifIndex and domain.<P>
123 
124 		@param	regType
125 					The registration type being browsed for followed by the protocol, separated by a
126 					dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
127 		<P>
128 		@param	listener
129 					This object will get called when instances of the service are discovered (or disappear).
130 		<P>
131 		@return		A {@link DNSSDService} that represents the active browse operation.
132 
133 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
134 		@see    RuntimePermission
135 	*/
browse( String regType, BrowseListener listener)136 	public static DNSSDService	browse( String regType, BrowseListener listener)
137 	throws DNSSDException
138 	{ return browse( 0, 0, regType, "", listener); }
139 
140 	/** Resolve a service name discovered via browse() to a target host name, port number, and txt record.<P>
141 
142 		Note: Applications should NOT use resolve() solely for txt record monitoring - use
143 		queryRecord() instead, as it is more efficient for this task.<P>
144 
145 		Note: When the desired results have been returned, the client MUST terminate the resolve by
146 		calling {@link DNSSDService#stop}.<P>
147 
148 		Note: resolve() behaves correctly for typical services that have a single SRV record and
149  		a single TXT record (the TXT record may be empty.)  To resolve non-standard services with
150  		multiple SRV or TXT records, use queryRecord().<P>
151 
152 		@param	flags
153 					Currently ignored, reserved for future use.
154 		<P>
155 		@param	ifIndex
156 					The interface on which to resolve the service.  The client should
157 					pass the interface on which the serviceName was discovered (i.e.
158 					the ifIndex passed to the serviceFound() callback)
159 					or 0 to resolve the named service on all available interfaces.
160 		<P>
161 		@param	serviceName
162 					The servicename to be resolved.
163 		<P>
164 		@param	regType
165 					The registration type being resolved followed by the protocol, separated by a
166 					dot (e.g. "_ftp._tcp").  The transport protocol must be "_tcp" or "_udp".
167 		<P>
168 		@param	domain
169 					The domain on which the service is registered, i.e. the domain passed
170 					to the serviceFound() callback.
171 		<P>
172 		@param	listener
173 					This object will get called when the service is resolved.
174 		<P>
175 		@return		A {@link DNSSDService} that represents the active resolve operation.
176 
177 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
178 		@see    RuntimePermission
179 	*/
resolve( int flags, int ifIndex, String serviceName, String regType, String domain, ResolveListener listener)180 	public static DNSSDService	resolve( int flags, int ifIndex, String serviceName, String regType,
181 										String domain, ResolveListener listener)
182 	throws DNSSDException
183 	{ return getInstance()._resolve( flags, ifIndex, serviceName, regType, domain, listener); }
184 
185 	/** Register a service, to be discovered via browse() and resolve() calls.<P>
186 		@param	flags
187 					Possible values are: NO_AUTO_RENAME.
188 		<P>
189 		@param	ifIndex
190 					If non-zero, specifies the interface on which to register the service
191 					(the index for a given interface is determined via the if_nametoindex()
192 					family of calls.)  Most applications will pass 0 to register on all
193 					available interfaces.  Pass -1 to register a service only on the local
194 					machine (service will not be visible to remote hosts).
195 		<P>
196 		@param	serviceName
197 					If non-null, specifies the service name to be registered.
198 					Applications need not specify a name, in which case the
199 					computer name is used (this name is communicated to the client via
200 					the serviceRegistered() callback).
201 		<P>
202 		@param	regType
203 					The registration type being registered followed by the protocol, separated by a
204 					dot (e.g. "_ftp._tcp").  The transport protocol must be "_tcp" or "_udp".
205 		<P>
206 		@param	domain
207 					If non-null, specifies the domain on which to advertise the service.
208 					Most applications will not specify a domain, instead automatically
209 					registering in the default domain(s).
210 		<P>
211 		@param	host
212 					If non-null, specifies the SRV target host name.  Most applications
213 					will not specify a host, instead automatically using the machine's
214 					default host name(s).  Note that specifying a non-null host does NOT
215 					create an address record for that host - the application is responsible
216 					for ensuring that the appropriate address record exists, or creating it
217 					via {@link DNSSDRegistration#addRecord}.
218 		<P>
219 		@param	port
220 					The port on which the service accepts connections.  Pass 0 for a
221 					"placeholder" service (i.e. a service that will not be discovered by
222 					browsing, but will cause a name conflict if another client tries to
223 					register that same name.)  Most clients will not use placeholder services.
224 		<P>
225 		@param	txtRecord
226 					The txt record rdata.  May be null.  Note that a non-null txtRecord
227 					MUST be a properly formatted DNS TXT record, i.e. &lt;length byte&gt; &lt;data&gt;
228 					&lt;length byte&gt; &lt;data&gt; ...
229 		<P>
230 		@param	listener
231 					This object will get called when the service is registered.
232 		<P>
233 		@return		A {@link DNSSDRegistration} that controls the active registration.
234 
235 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
236 		@see    RuntimePermission
237 	*/
register( int flags, int ifIndex, String serviceName, String regType, String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener)238 	public static DNSSDRegistration	register( int flags, int ifIndex, String serviceName, String regType,
239 									String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener)
240 	throws DNSSDException
241 	{ return getInstance()._register( flags, ifIndex, serviceName, regType, domain, host, port, txtRecord, listener); }
242 
243 	/** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.<P>
244 		@param	serviceName
245 					If non-null, specifies the service name to be registered.
246 					Applications need not specify a name, in which case the
247 					computer name is used (this name is communicated to the client via
248 					the serviceRegistered() callback).
249 		<P>
250 		@param	regType
251 					The registration type being registered followed by the protocol, separated by a
252 					dot (e.g. "_ftp._tcp").  The transport protocol must be "_tcp" or "_udp".
253 		<P>
254 		@param	port
255 					The port on which the service accepts connections.  Pass 0 for a
256 					"placeholder" service (i.e. a service that will not be discovered by
257 					browsing, but will cause a name conflict if another client tries to
258 					register that same name.)  Most clients will not use placeholder services.
259 		<P>
260 		@param	listener
261 					This object will get called when the service is registered.
262 		<P>
263 		@return		A {@link DNSSDRegistration} that controls the active registration.
264 
265 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
266 		@see    RuntimePermission
267 	*/
register( String serviceName, String regType, int port, RegisterListener listener)268 	public static DNSSDRegistration	register( String serviceName, String regType, int port, RegisterListener listener)
269 	throws DNSSDException
270 	{ return register( 0, 0, serviceName, regType, null, null, port, null, listener); }
271 
272 	/** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of
273 		multiple individual records.<P>
274 		<P>
275 		@return		A {@link DNSSDRecordRegistrar} that can be used to register records.
276 
277 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
278 		@see    RuntimePermission
279 	*/
createRecordRegistrar( RegisterRecordListener listener)280 	public static DNSSDRecordRegistrar	createRecordRegistrar( RegisterRecordListener listener)
281 	throws DNSSDException
282 	{ return getInstance()._createRecordRegistrar( listener); }
283 
284 	/** Query for an arbitrary DNS record.<P>
285 		@param	flags
286 					Possible values are: MORE_COMING.
287 		<P>
288 		@param	ifIndex
289 					If non-zero, specifies the interface on which to issue the query
290 					(the index for a given interface is determined via the if_nametoindex()
291 					family of calls.)  Passing 0 causes the name to be queried for on all
292 					interfaces.  Passing -1 causes the name to be queried for only on the
293 					local host.
294 		<P>
295 		@param	serviceName
296 					The full domain name of the resource record to be queried for.
297 		<P>
298 		@param	rrtype
299 					The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
300 					as defined in nameser.h.
301 		<P>
302 		@param	rrclass
303 					The class of the resource record, as defined in nameser.h
304 					(usually 1 for the Internet class).
305 		<P>
306 		@param	listener
307 					This object will get called when the query completes.
308 		<P>
309 		@return		A {@link DNSSDService} that controls the active query.
310 
311 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
312 		@see    RuntimePermission
313 	*/
queryRecord( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener listener)314 	public static DNSSDService	queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
315 										int rrclass, QueryListener listener)
316 	throws DNSSDException
317 	{ return getInstance()._queryRecord( flags, ifIndex, serviceName, rrtype, rrclass, listener); }
318 
319 	/** Asynchronously enumerate domains available for browsing and registration.<P>
320 
321 		Currently, the only domain returned is "local.", but other domains will be returned in future.<P>
322 
323 		The enumeration MUST be cancelled by calling {@link DNSSDService#stop} when no more domains
324 		are to be found.<P>
325 		@param	flags
326 					Possible values are: BROWSE_DOMAINS, REGISTRATION_DOMAINS.
327 		<P>
328 		@param	ifIndex
329 					If non-zero, specifies the interface on which to look for domains.
330 					(the index for a given interface is determined via the if_nametoindex()
331 					family of calls.)  Most applications will pass 0 to enumerate domains on
332 					all interfaces.
333 		<P>
334 		@param	listener
335 					This object will get called when domains are found.
336 		<P>
337 		@return		A {@link DNSSDService} that controls the active enumeration.
338 
339 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
340 		@see    RuntimePermission
341 	*/
enumerateDomains( int flags, int ifIndex, DomainListener listener)342 	public static DNSSDService	enumerateDomains( int flags, int ifIndex, DomainListener listener)
343 	throws DNSSDException
344 	{ return getInstance()._enumerateDomains( flags, ifIndex, listener); }
345 
346 	/**	Concatenate a three-part domain name (as provided to the listeners) into a
347 		properly-escaped full domain name. Note that strings passed to listeners are
348 		ALREADY ESCAPED where necessary.<P>
349 		@param	serviceName
350 					The service name - any dots or slashes must NOT be escaped.
351 					May be null (to construct a PTR record name, e.g. "_ftp._tcp.apple.com").
352 		<P>
353 		@param	regType
354 					The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").
355 		<P>
356 		@param	domain
357 					The domain name, e.g. "apple.com".  Any literal dots or backslashes must be escaped.
358 		<P>
359 		@return		The full domain name.
360 
361 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
362 		@see    RuntimePermission
363 	*/
constructFullName( String serviceName, String regType, String domain)364 	public static String		constructFullName( String serviceName, String regType, String domain)
365 	throws DNSSDException
366 	{ return getInstance()._constructFullName( serviceName, regType, domain); }
367 
368 	/** Instruct the daemon to verify the validity of a resource record that appears to
369 		be out of date. (e.g. because tcp connection to a service's target failed.) <P>
370 
371 		Causes the record to be flushed from the daemon's cache (as well as all other
372 		daemons' caches on the network) if the record is determined to be invalid.<P>
373 		@param	flags
374 					Currently unused, reserved for future use.
375 		<P>
376 		@param	ifIndex
377 					If non-zero, specifies the interface on which to reconfirm the record
378 					(the index for a given interface is determined via the if_nametoindex()
379 					family of calls.)  Passing 0 causes the name to be reconfirmed on all
380 					interfaces.  Passing -1 causes the name to be reconfirmed only on the
381 					local host.
382 		<P>
383 		@param	fullName
384 					The resource record's full domain name.
385 		<P>
386 		@param	rrtype
387 					The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
388 		<P>
389 		@param	rrclass
390 					The class of the resource record, as defined in nameser.h (usually 1).
391 		<P>
392 		@param	rdata
393 					The raw rdata of the resource record.
394 
395 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
396 		@see    RuntimePermission
397 	*/
reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata)398 	public static void		reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
399 										int rrclass, byte[] rdata)
400 	{ getInstance()._reconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); }
401 
402 	/** Return the canonical name of a particular interface index.<P>
403 		@param	ifIndex
404 					A valid interface index. Must not be ALL_INTERFACES.
405 		<P>
406 		@return		The name of the interface, which should match java.net.NetworkInterface.getName().
407 
408 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
409 		@see    RuntimePermission
410 	*/
getNameForIfIndex( int ifIndex)411 	public static String	getNameForIfIndex( int ifIndex)
412 	{ return getInstance()._getNameForIfIndex( ifIndex); }
413 
414 	/** Return the index of a named interface.<P>
415 		@param	ifName
416 					A valid interface name. An example is java.net.NetworkInterface.getName().
417 		<P>
418 		@return		The interface index.
419 
420 		@throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
421 		@see    RuntimePermission
422 	*/
getIfIndexForName( String ifName)423 	public static int		getIfIndexForName( String ifName)
424 	{ return getInstance()._getIfIndexForName( ifName); }
425 
DNSSD()426 	protected						DNSSD() {}	// prevent direct instantiation
427 
428 	/** Return the single instance of DNSSD. */
getInstance()429 	static protected final DNSSD	getInstance()
430 	{
431 		SecurityManager sm = System.getSecurityManager();
432         if (sm != null)
433             sm.checkPermission( new RuntimePermission( "getDNSSDInstance"));
434 		 return fInstance;
435 	}
436 
_makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener listener)437 	abstract protected DNSSDService	_makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener listener)
438 	throws DNSSDException;
439 
_resolve( int flags, int ifIndex, String serviceName, String regType, String domain, ResolveListener listener)440 	abstract protected DNSSDService	_resolve( int flags, int ifIndex, String serviceName, String regType,
441 										String domain, ResolveListener listener)
442 	throws DNSSDException;
443 
_register( int flags, int ifIndex, String serviceName, String regType, String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener)444 	abstract protected DNSSDRegistration	_register( int flags, int ifIndex, String serviceName, String regType,
445 									String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener)
446 	throws DNSSDException;
447 
_createRecordRegistrar( RegisterRecordListener listener)448 	abstract protected DNSSDRecordRegistrar	_createRecordRegistrar( RegisterRecordListener listener)
449 	throws DNSSDException;
450 
_queryRecord( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener listener)451 	abstract protected DNSSDService	_queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
452 										int rrclass, QueryListener listener)
453 	throws DNSSDException;
454 
_enumerateDomains( int flags, int ifIndex, DomainListener listener)455 	abstract protected DNSSDService	_enumerateDomains( int flags, int ifIndex, DomainListener listener)
456 	throws DNSSDException;
457 
_constructFullName( String serviceName, String regType, String domain)458 	abstract protected String		_constructFullName( String serviceName, String regType, String domain)
459 	throws DNSSDException;
460 
_reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata)461 	abstract protected void			_reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
462 										int rrclass, byte[] rdata);
463 
_getNameForIfIndex( int ifIndex)464 	abstract protected String		_getNameForIfIndex( int ifIndex);
465 
_getIfIndexForName( String ifName)466 	abstract protected int			_getIfIndexForName( String ifName);
467 
468 	protected static DNSSD			fInstance;
469 
470 	static
471 	{
472 		try
473 		{
474 			String name = System.getProperty( "com.apple.dnssd.DNSSD" );
475 			if (name == null)
476 				name = "com.apple.dnssd.AppleDNSSD";	// Fall back to Apple-provided class.
477 			fInstance = (DNSSD) Class.forName(name).newInstance();
478 		}
479 		catch( Exception e )
480 		{
481 			throw new InternalError( "cannot instantiate DNSSD" + e );
482 		}
483 	}
484 }
485 
486 
487 // Concrete implementation of DNSSDException
488 class	AppleDNSSDException extends DNSSDException
489 {
AppleDNSSDException( int errorCode)490 	public						AppleDNSSDException( int errorCode) { fErrorCode = errorCode; }
491 
getErrorCode()492 	public int					getErrorCode() { return fErrorCode; }
493 
getMessage()494 	public String				getMessage()
495 	{
496 		final String	kMessages[] = {		// should probably be put into a resource or something
497 			"UNKNOWN",
498 			"NO_SUCH_NAME",
499 			"NO_MEMORY",
500 			"BAD_PARAM",
501 			"BAD_REFERENCE",
502 			"BAD_STATE",
503 			"BAD_FLAGS",
504 			"UNSUPPORTED",
505 			"NOT_INITIALIZED",
506 			"NO_CACHE",
507 			"ALREADY_REGISTERED",
508 			"NAME_CONFLICT",
509 			"INVALID",
510 			"FIREWALL",
511 			"INCOMPATIBLE",
512 			"BAD_INTERFACE_INDEX",
513 			"REFUSED",
514 			"NOSUCHRECORD",
515 			"NOAUTH",
516 			"NOSUCHKEY",
517 			"NATTRAVERSAL",
518 			"DOUBLENAT",
519 			"BADTIME",
520 			"BADSIG",
521 			"BADKEY",
522 			"TRANSIENT",
523 			"SERVICENOTRUNNING",
524 			"NATPORTMAPPINGUNSUPPORTED",
525 			"NATPORTMAPPINGDISABLED"
526 		};
527 
528 		if (fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length))
529 		{
530 			return "DNS-SD Error " + String.valueOf( fErrorCode) + ": " + kMessages[ UNKNOWN - fErrorCode];
531 		}
532 		else
533 			return super.getMessage() + "(" + String.valueOf( fErrorCode) + ")";
534 	}
535 
536 	protected int			fErrorCode;
537 }
538 
539 // The concrete, default implementation.
540 class	AppleDNSSD extends DNSSD
541 {
542 	static
543 	{
544 		System.loadLibrary( "jdns_sd");
545 
546 		int		libInitResult = InitLibrary( 2);	// Current version number (must be sync'd with jnilib version)
547 
548 		if (libInitResult != DNSSDException.NO_ERROR)
549 			throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult).getMessage());
550 	}
551 
552 	static public boolean	hasAutoCallbacks;	// Set by InitLibrary() to value of AUTO_CALLBACKS
553 
_makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)554 	protected DNSSDService	_makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)
555 	throws DNSSDException
556 	{
557 		return new AppleBrowser( flags, ifIndex, regType, domain, client);
558 	}
559 
_resolve( int flags, int ifIndex, String serviceName, String regType, String domain, ResolveListener client)560 	protected DNSSDService	_resolve( int flags, int ifIndex, String serviceName, String regType,
561 										String domain, ResolveListener client)
562 	throws DNSSDException
563 	{
564 		return new AppleResolver( flags, ifIndex, serviceName, regType, domain, client);
565 	}
566 
_register( int flags, int ifIndex, String serviceName, String regType, String domain, String host, int port, TXTRecord txtRecord, RegisterListener client)567 	protected DNSSDRegistration	_register( int flags, int ifIndex, String serviceName, String regType,
568 									String domain, String host, int port, TXTRecord txtRecord, RegisterListener client)
569 	throws DNSSDException
570 	{
571 		return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port,
572 										( txtRecord != null) ? txtRecord.getRawBytes() : null, client);
573 	}
574 
_createRecordRegistrar( RegisterRecordListener listener)575 	protected DNSSDRecordRegistrar	_createRecordRegistrar( RegisterRecordListener listener)
576 	throws DNSSDException
577 	{
578 		return new AppleRecordRegistrar( listener);
579 	}
580 
_queryRecord( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener client)581 	protected DNSSDService		_queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
582 										int rrclass, QueryListener client)
583 	throws DNSSDException
584 	{
585 		return new AppleQuery( flags, ifIndex, serviceName, rrtype, rrclass, client);
586 	}
587 
_enumerateDomains( int flags, int ifIndex, DomainListener listener)588 	protected DNSSDService		_enumerateDomains( int flags, int ifIndex, DomainListener listener)
589 	throws DNSSDException
590 	{
591 		return new AppleDomainEnum( flags, ifIndex, listener);
592 	}
593 
_constructFullName( String serviceName, String regType, String domain)594 	protected String			_constructFullName( String serviceName, String regType, String domain)
595 	throws DNSSDException
596 	{
597 		String[]	responseHolder = new String[1];	// lame maneuver to get around Java's lack of reference parameters
598 
599 		int rc = ConstructName( serviceName, regType, domain, responseHolder);
600 		if (rc != 0)
601 			throw new AppleDNSSDException( rc);
602 
603 		return responseHolder[0];
604 	}
605 
_reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata)606 	protected void				_reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
607 										int rrclass, byte[] rdata)
608 	{
609 		ReconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata);
610 	}
611 
_getNameForIfIndex( int ifIndex)612 	protected String			_getNameForIfIndex( int ifIndex)
613 	{
614 		return GetNameForIfIndex( ifIndex);
615 	}
616 
_getIfIndexForName( String ifName)617 	protected int				_getIfIndexForName( String ifName)
618 	{
619 		return GetIfIndexForName( ifName);
620 	}
621 
622 
ConstructName( String serviceName, String regType, String domain, String[] pOut)623 	protected native int	ConstructName( String serviceName, String regType, String domain, String[] pOut);
624 
ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata)625 	protected native void	ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
626 										int rrclass, byte[] rdata);
627 
GetNameForIfIndex( int ifIndex)628 	protected native String	GetNameForIfIndex( int ifIndex);
629 
GetIfIndexForName( String ifName)630 	protected native int	GetIfIndexForName( String ifName);
631 
InitLibrary( int callerVersion)632 	protected static native int	InitLibrary( int callerVersion);
633 }
634 
635 class	AppleService implements DNSSDService, Runnable
636 {
AppleService(BaseListener listener)637 	public					AppleService(BaseListener listener)	{ fNativeContext = 0; fListener = listener; }
638 
stop()639 	public void				stop() { this.HaltOperation(); }
640 
641 	/* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
BlockForData()642 	protected native int	BlockForData();
643 
644 	/* Call ProcessResults when data appears on socket descriptor. */
ProcessResults()645 	protected native int	ProcessResults();
646 
HaltOperation()647 	protected synchronized native void HaltOperation();
648 
ThrowOnErr( int rc)649 	protected void			ThrowOnErr( int rc) throws DNSSDException
650 	{
651 		if (rc != 0)
652 			throw new AppleDNSSDException( rc);
653 	}
654 
655 	protected long	/* warning */	fNativeContext;		// Private storage for native side
656 
run()657 	public void		run()
658 	{
659 		while ( true )
660 		{
661 			// Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to
662 			// block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized
663 			// we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread,
664 			// and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD
665 			// operation. The Unix kernel always allocates the lowest available file descriptor to a new socket,
666 			// so the same file descriptor is highly likely to be reused for the new operation, and if our old
667 			// stale ServiceThread accidentally consumes bytes off that new socket we'll get really messed up.
668 			// To guard against that, before calling ProcessResults we check to ensure that our
669 			// fNativeContext has not been deleted, which is a telltale sign that our operation was stopped.
670 			// After calling ProcessResults we check again, because it's extremely common for callback
671 			// functions to stop their own operation and start others. For example, a resolveListener callback
672 			// may well stop the resolve and then start a QueryRecord call to monitor the TXT record.
673 			//
674 			// The remaining risk is that between our checking fNativeContext and calling ProcessResults(),
675 			// some other thread could stop the operation and start a new one using same file descriptor, and
676 			// we wouldn't know. To prevent this, the AppleService object's HaltOperation() routine is declared
677 			// synchronized and we perform our checks synchronized on the AppleService object, which ensures
678 			// that HaltOperation() can't execute while we're doing it. Because Java locks are re-entrant this
679 			// locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent
680 			// any other thread from stopping it until after the callback has completed and returned to us here.
681 
682 			int result = BlockForData();
683 			synchronized (this)
684 			{
685 				if (fNativeContext == 0) break;	// Some other thread stopped our DNSSD operation; time to terminate this thread
686 				if (result == 0) continue;		// If BlockForData() said there was no data, go back and block again
687 				result = ProcessResults();
688 				if (fNativeContext == 0) break;	// Event listener stopped its own DNSSD operation; terminate this thread
689 				if (result != 0) { fListener.operationFailed(this, result); break; }	// If error, notify listener
690 			}
691 		}
692 	}
693 
694 	protected BaseListener fListener;
695 }
696 
697 
698 class	AppleBrowser extends AppleService
699 {
AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)700 	public			AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)
701 	throws DNSSDException
702 	{
703 		super(client);
704 		this.ThrowOnErr( this.CreateBrowser( flags, ifIndex, regType, domain));
705 		if (!AppleDNSSD.hasAutoCallbacks)
706 			new Thread(this).start();
707 	}
708 
709 	// Sets fNativeContext. Returns non-zero on error.
CreateBrowser( int flags, int ifIndex, String regType, String domain)710 	protected native int	CreateBrowser( int flags, int ifIndex, String regType, String domain);
711 }
712 
713 class	AppleResolver extends AppleService
714 {
AppleResolver( int flags, int ifIndex, String serviceName, String regType, String domain, ResolveListener client)715 	public			AppleResolver( int flags, int ifIndex, String serviceName, String regType,
716 									String domain, ResolveListener client)
717 	throws DNSSDException
718 	{
719 		super(client);
720 		this.ThrowOnErr( this.CreateResolver( flags, ifIndex, serviceName, regType, domain));
721 		if (!AppleDNSSD.hasAutoCallbacks)
722 			new Thread(this).start();
723 	}
724 
725 	// Sets fNativeContext. Returns non-zero on error.
CreateResolver( int flags, int ifIndex, String serviceName, String regType, String domain)726 	protected native int	CreateResolver( int flags, int ifIndex, String serviceName, String regType,
727 											String domain);
728 }
729 
730 // An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord.
731 class	AppleDNSRecord implements DNSRecord
732 {
AppleDNSRecord( AppleService owner)733 	public			AppleDNSRecord( AppleService owner)
734 	{
735 		fOwner = owner;
736 		fRecord = 0; 		// record always starts out empty
737 	}
738 
update( int flags, byte[] rData, int ttl)739 	public void			update( int flags, byte[] rData, int ttl)
740 	throws DNSSDException
741 	{
742 		this.ThrowOnErr( this.Update( flags, rData, ttl));
743 	}
744 
remove()745 	public void			remove()
746 	throws DNSSDException
747 	{
748 		this.ThrowOnErr( this.Remove());
749 	}
750 
751 	protected long			fRecord;		// Really a DNSRecord; sizeof(long) == sizeof(void*) ?
752 	protected AppleService	fOwner;
753 
ThrowOnErr( int rc)754 	protected void			ThrowOnErr( int rc) throws DNSSDException
755 	{
756 		if (rc != 0)
757 			throw new AppleDNSSDException( rc);
758 	}
759 
Update( int flags, byte[] rData, int ttl)760 	protected native int	Update( int flags, byte[] rData, int ttl);
761 
Remove()762 	protected native int	Remove();
763 }
764 
765 class	AppleRegistration extends AppleService implements DNSSDRegistration
766 {
AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain, String host, int port, byte[] txtRecord, RegisterListener client)767 	public			AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain,
768 								String host, int port, byte[] txtRecord, RegisterListener client)
769 	throws DNSSDException
770 	{
771 		super(client);
772 		this.ThrowOnErr( this.BeginRegister( ifIndex, flags, serviceName, regType, domain, host, port, txtRecord));
773 		if (!AppleDNSSD.hasAutoCallbacks)
774 			new Thread(this).start();
775 	}
776 
addRecord( int flags, int rrType, byte[] rData, int ttl)777 	public DNSRecord	addRecord( int flags, int rrType, byte[] rData, int ttl)
778 	throws DNSSDException
779 	{
780 		AppleDNSRecord	newRecord = new AppleDNSRecord( this);
781 
782 		this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord));
783 		return newRecord;
784 	}
785 
getTXTRecord()786 	public DNSRecord	getTXTRecord()
787 	throws DNSSDException
788 	{
789 		return new AppleDNSRecord( this);	// A record with ref 0 is understood to be primary TXT record
790 	}
791 
792 	// Sets fNativeContext. Returns non-zero on error.
BeginRegister( int ifIndex, int flags, String serviceName, String regType, String domain, String host, int port, byte[] txtRecord)793 	protected native int	BeginRegister( int ifIndex, int flags, String serviceName, String regType,
794 											String domain, String host, int port, byte[] txtRecord);
795 
796 	// Sets fNativeContext. Returns non-zero on error.
AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj)797 	protected native int	AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj);
798 }
799 
800 class	AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar
801 {
AppleRecordRegistrar( RegisterRecordListener listener)802 	public			AppleRecordRegistrar( RegisterRecordListener listener)
803 	throws DNSSDException
804 	{
805 		super(listener);
806 		this.ThrowOnErr( this.CreateConnection());
807 		if (!AppleDNSSD.hasAutoCallbacks)
808 			new Thread(this).start();
809 	}
810 
registerRecord( int flags, int ifIndex, String fullname, int rrtype, int rrclass, byte[] rdata, int ttl)811 	public DNSRecord	registerRecord( int flags, int ifIndex, String fullname, int rrtype,
812 									int rrclass, byte[] rdata, int ttl)
813 	throws DNSSDException
814 	{
815 		AppleDNSRecord	newRecord = new AppleDNSRecord( this);
816 
817 		this.ThrowOnErr( this.RegisterRecord( flags, ifIndex, fullname, rrtype, rrclass, rdata, ttl, newRecord));
818 		return newRecord;
819 	}
820 
821 	// Sets fNativeContext. Returns non-zero on error.
CreateConnection()822 	protected native int	CreateConnection();
823 
824 	// Sets fNativeContext. Returns non-zero on error.
RegisterRecord( int flags, int ifIndex, String fullname, int rrtype, int rrclass, byte[] rdata, int ttl, AppleDNSRecord destObj)825 	protected native int	RegisterRecord( int flags, int ifIndex, String fullname, int rrtype,
826 										int rrclass, byte[] rdata, int ttl, AppleDNSRecord destObj);
827 }
828 
829 class	AppleQuery extends AppleService
830 {
AppleQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener client)831 	public			AppleQuery( int flags, int ifIndex, String serviceName, int rrtype,
832 										int rrclass, QueryListener client)
833 	throws DNSSDException
834 	{
835 		super(client);
836 		this.ThrowOnErr( this.CreateQuery( flags, ifIndex, serviceName, rrtype, rrclass));
837 		if (!AppleDNSSD.hasAutoCallbacks)
838 			new Thread(this).start();
839 	}
840 
841 	// Sets fNativeContext. Returns non-zero on error.
CreateQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass)842 	protected native int	CreateQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass);
843 }
844 
845 class	AppleDomainEnum extends AppleService
846 {
AppleDomainEnum( int flags, int ifIndex, DomainListener client)847 	public			AppleDomainEnum( int flags, int ifIndex, DomainListener client)
848 	throws DNSSDException
849 	{
850 		super(client);
851 		this.ThrowOnErr( this.BeginEnum( flags, ifIndex));
852 		if (!AppleDNSSD.hasAutoCallbacks)
853 			new Thread(this).start();
854 	}
855 
856 	// Sets fNativeContext. Returns non-zero on error.
BeginEnum( int flags, int ifIndex)857 	protected native int	BeginEnum( int flags, int ifIndex);
858 }
859 
860 
861