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