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