1 /* -*- Mode: Java; tab-width: 4 -*-
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 Change History (most recent first):
20 Revision 1.16 2008/11/04 20:06:20 cheshire
21 <rdar://problem/6186231> Change MAX_DOMAIN_NAME to 256
23 Revision 1.15 2007/03/13 00:28:03 vazquez
24 <rdar://problem/4625928> Java: Rename exported symbols in libjdns_sd.jnilib
26 Revision 1.14 2007/03/13 00:10:14 vazquez
27 <rdar://problem/4455206> Java: 64 bit JNI patch
29 Revision 1.13 2007/02/24 23:08:02 mkrochma
30 <rdar://problem/5001673> Typo in Bonjour Java API document
32 Revision 1.12 2007/02/09 00:33:02 cheshire
33 Add missing error codes to kMessages array
35 Revision 1.11 2006/08/14 23:25:08 cheshire
36 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
38 Revision 1.10 2006/06/20 23:05:55 rpantos
39 <rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent
41 Revision 1.9 2005/10/26 01:52:24 cheshire
42 <rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux)
44 Revision 1.8 2005/07/11 01:55:21 cheshire
45 <rdar://problem/4175511> Race condition in Java API
47 Revision 1.7 2005/07/05 13:01:52 cheshire
48 <rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time
50 Revision 1.6 2005/07/05 00:02:25 cheshire
53 Revision 1.5 2005/07/04 21:13:47 cheshire
54 Add missing error message strings
56 Revision 1.4 2004/12/11 03:00:59 rpantos
57 <rdar://problem/3907498> Java DNSRecord API should be cleaned up
59 Revision 1.3 2004/11/12 03:23:08 rpantos
60 rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex.
62 Revision 1.2 2004/05/20 17:43:18 cheshire
63 Fix invalid UTF-8 characters in file
65 Revision 1.1 2004/04/30 16:32:34 rpantos
69 This file declares and implements DNSSD, the central Java factory class
70 for doing DNS Service Discovery. It includes the mostly-abstract public
71 interface, as well as the Apple* implementation subclasses.
75 package com
.apple
.dnssd
;
79 DNSSD provides access to DNS Service Discovery features of ZeroConf networking.<P>
81 It is a factory class that is used to invoke registration and discovery-related
82 operations. Most operations are non-blocking; clients are called back through an interface
83 with the result of an operation. Callbacks are made from a separate worker thread.<P>
85 For example, in this program<P>
87 class MyClient implements BrowseListener {
88 void lookForWebServers() {
89 myBrowser = DNSSD.browse("_http._tcp", this);
92 public void serviceFound(DNSSDService browser, int flags, int ifIndex,
93 String serviceName, String regType, String domain) {}
96 <CODE>MyClient.serviceFound()</CODE> would be called for every HTTP server discovered in the
97 default browse domain(s).
100 abstract public class DNSSD
102 /** Flag indicates to a {@link BrowseListener} that another result is
103 queued. Applications should not update their UI to display browse
104 results if the MORE_COMING flag is set; they will be called at least once
105 more with the flag clear.
107 public static final int MORE_COMING
= ( 1 << 0 );
109 /** If flag is set in a {@link DomainListener} callback, indicates that the result is the default domain. */
110 public static final int DEFAULT
= ( 1 << 2 );
112 /** If flag is set, a name conflict will trigger an exception when registering non-shared records.<P>
113 A name must be explicitly specified when registering a service if this bit is set
114 (i.e. the default name may not not be used).
116 public static final int NO_AUTO_RENAME
= ( 1 << 3 );
118 /** If flag is set, allow multiple records with this name on the network (e.g. PTR records)
119 when registering individual records on a {@link DNSSDRegistration}.
121 public static final int SHARED
= ( 1 << 4 );
123 /** If flag is set, records with this name must be unique on the network (e.g. SRV records). */
124 public static final int UNIQUE
= ( 1 << 5 );
126 /** Set flag when calling enumerateDomains() to restrict results to domains recommended for browsing. */
127 public static final int BROWSE_DOMAINS
= ( 1 << 6 );
128 /** Set flag when calling enumerateDomains() to restrict results to domains recommended for registration. */
129 public static final int REGISTRATION_DOMAINS
= ( 1 << 7 );
131 /** Maximum length, in bytes, of a domain name represented as an escaped C-String. */
132 public static final int MAX_DOMAIN_NAME
= 1009;
134 /** Pass for ifIndex to specify all available interfaces. */
135 public static final int ALL_INTERFACES
= 0;
137 /** Pass for ifIndex to specify the localhost interface. */
138 public static final int LOCALHOST_ONLY
= -1;
140 /** Browse for instances of a service.<P>
142 Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P>
145 Currently ignored, reserved for future use.
148 If non-zero, specifies the interface on which to browse for services
149 (the index for a given interface is determined via the if_nametoindex()
150 family of calls.) Most applications will pass 0 to browse on all available
151 interfaces. Pass -1 to only browse for services provided on the local host.
154 The registration type being browsed for followed by the protocol, separated by a
155 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
158 If non-null, specifies the domain on which to browse for services.
159 Most applications will not specify a domain, instead browsing on the
163 This object will get called when instances of the service are discovered (or disappear).
165 @return A {@link DNSSDService} that represents the active browse operation.
167 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
168 @see RuntimePermission
170 public static DNSSDService
browse( int flags
, int ifIndex
, String regType
, String domain
, BrowseListener listener
)
171 throws DNSSDException
172 { return getInstance()._makeBrowser( flags
, ifIndex
, regType
, domain
, listener
); }
174 /** Browse for instances of a service. Use default flags, ifIndex and domain.<P>
177 The registration type being browsed for followed by the protocol, separated by a
178 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
181 This object will get called when instances of the service are discovered (or disappear).
183 @return A {@link DNSSDService} that represents the active browse operation.
185 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
186 @see RuntimePermission
188 public static DNSSDService
browse( String regType
, BrowseListener listener
)
189 throws DNSSDException
190 { return browse( 0, 0, regType
, "", listener
); }
192 /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.<P>
194 Note: Applications should NOT use resolve() solely for txt record monitoring - use
195 queryRecord() instead, as it is more efficient for this task.<P>
197 Note: When the desired results have been returned, the client MUST terminate the resolve by
198 calling {@link DNSSDService#stop}.<P>
200 Note: resolve() behaves correctly for typical services that have a single SRV record and
201 a single TXT record (the TXT record may be empty.) To resolve non-standard services with
202 multiple SRV or TXT records, use queryRecord().<P>
205 Currently ignored, reserved for future use.
208 The interface on which to resolve the service. The client should
209 pass the interface on which the serviceName was discovered (i.e.
210 the ifIndex passed to the serviceFound() callback)
211 or 0 to resolve the named service on all available interfaces.
214 The servicename to be resolved.
217 The registration type being resolved followed by the protocol, separated by a
218 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
221 The domain on which the service is registered, i.e. the domain passed
222 to the serviceFound() callback.
225 This object will get called when the service is resolved.
227 @return A {@link DNSSDService} that represents the active resolve operation.
229 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
230 @see RuntimePermission
232 public static DNSSDService
resolve( int flags
, int ifIndex
, String serviceName
, String regType
,
233 String domain
, ResolveListener listener
)
234 throws DNSSDException
235 { return getInstance()._resolve( flags
, ifIndex
, serviceName
, regType
, domain
, listener
); }
237 /** Register a service, to be discovered via browse() and resolve() calls.<P>
239 Possible values are: NO_AUTO_RENAME.
242 If non-zero, specifies the interface on which to register the service
243 (the index for a given interface is determined via the if_nametoindex()
244 family of calls.) Most applications will pass 0 to register on all
245 available interfaces. Pass -1 to register a service only on the local
246 machine (service will not be visible to remote hosts).
249 If non-null, specifies the service name to be registered.
250 Applications need not specify a name, in which case the
251 computer name is used (this name is communicated to the client via
252 the serviceRegistered() callback).
255 The registration type being registered followed by the protocol, separated by a
256 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
259 If non-null, specifies the domain on which to advertise the service.
260 Most applications will not specify a domain, instead automatically
261 registering in the default domain(s).
264 If non-null, specifies the SRV target host name. Most applications
265 will not specify a host, instead automatically using the machine's
266 default host name(s). Note that specifying a non-null host does NOT
267 create an address record for that host - the application is responsible
268 for ensuring that the appropriate address record exists, or creating it
269 via {@link DNSSDRegistration#addRecord}.
272 The port on which the service accepts connections. Pass 0 for a
273 "placeholder" service (i.e. a service that will not be discovered by
274 browsing, but will cause a name conflict if another client tries to
275 register that same name.) Most clients will not use placeholder services.
278 The txt record rdata. May be null. Note that a non-null txtRecord
279 MUST be a properly formatted DNS TXT record, i.e. <length byte> <data>
280 <length byte> <data> ...
283 This object will get called when the service is registered.
285 @return A {@link DNSSDRegistration} that controls the active registration.
287 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
288 @see RuntimePermission
290 public static DNSSDRegistration
register( int flags
, int ifIndex
, String serviceName
, String regType
,
291 String domain
, String host
, int port
, TXTRecord txtRecord
, RegisterListener listener
)
292 throws DNSSDException
293 { return getInstance()._register( flags
, ifIndex
, serviceName
, regType
, domain
, host
, port
, txtRecord
, listener
); }
295 /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.<P>
297 If non-null, specifies the service name to be registered.
298 Applications need not specify a name, in which case the
299 computer name is used (this name is communicated to the client via
300 the serviceRegistered() callback).
303 The registration type being registered followed by the protocol, separated by a
304 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
307 The port on which the service accepts connections. Pass 0 for a
308 "placeholder" service (i.e. a service that will not be discovered by
309 browsing, but will cause a name conflict if another client tries to
310 register that same name.) Most clients will not use placeholder services.
313 This object will get called when the service is registered.
315 @return A {@link DNSSDRegistration} that controls the active registration.
317 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
318 @see RuntimePermission
320 public static DNSSDRegistration
register( String serviceName
, String regType
, int port
, RegisterListener listener
)
321 throws DNSSDException
322 { return register( 0, 0, serviceName
, regType
, null, null, port
, null, listener
); }
324 /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of
325 multiple individual records.<P>
327 @return A {@link DNSSDRecordRegistrar} that can be used to register records.
329 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
330 @see RuntimePermission
332 public static DNSSDRecordRegistrar
createRecordRegistrar( RegisterRecordListener listener
)
333 throws DNSSDException
334 { return getInstance()._createRecordRegistrar( listener
); }
336 /** Query for an arbitrary DNS record.<P>
338 Possible values are: MORE_COMING.
341 If non-zero, specifies the interface on which to issue the query
342 (the index for a given interface is determined via the if_nametoindex()
343 family of calls.) Passing 0 causes the name to be queried for on all
344 interfaces. Passing -1 causes the name to be queried for only on the
348 The full domain name of the resource record to be queried for.
351 The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
352 as defined in nameser.h.
355 The class of the resource record, as defined in nameser.h
356 (usually 1 for the Internet class).
359 This object will get called when the query completes.
361 @return A {@link DNSSDService} that controls the active query.
363 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
364 @see RuntimePermission
366 public static DNSSDService
queryRecord( int flags
, int ifIndex
, String serviceName
, int rrtype
,
367 int rrclass
, QueryListener listener
)
368 throws DNSSDException
369 { return getInstance()._queryRecord( flags
, ifIndex
, serviceName
, rrtype
, rrclass
, listener
); }
371 /** Asynchronously enumerate domains available for browsing and registration.<P>
373 Currently, the only domain returned is "local.", but other domains will be returned in future.<P>
375 The enumeration MUST be cancelled by calling {@link DNSSDService#stop} when no more domains
378 Possible values are: BROWSE_DOMAINS, REGISTRATION_DOMAINS.
381 If non-zero, specifies the interface on which to look for domains.
382 (the index for a given interface is determined via the if_nametoindex()
383 family of calls.) Most applications will pass 0 to enumerate domains on
387 This object will get called when domains are found.
389 @return A {@link DNSSDService} that controls the active enumeration.
391 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
392 @see RuntimePermission
394 public static DNSSDService
enumerateDomains( int flags
, int ifIndex
, DomainListener listener
)
395 throws DNSSDException
396 { return getInstance()._enumerateDomains( flags
, ifIndex
, listener
); }
398 /** Concatenate a three-part domain name (as provided to the listeners) into a
399 properly-escaped full domain name. Note that strings passed to listeners are
400 ALREADY ESCAPED where necessary.<P>
402 The service name - any dots or slashes must NOT be escaped.
403 May be null (to construct a PTR record name, e.g. "_ftp._tcp.apple.com").
406 The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").
409 The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped.
411 @return The full domain name.
413 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
414 @see RuntimePermission
416 public static String
constructFullName( String serviceName
, String regType
, String domain
)
417 throws DNSSDException
418 { return getInstance()._constructFullName( serviceName
, regType
, domain
); }
420 /** Instruct the daemon to verify the validity of a resource record that appears to
421 be out of date. (e.g. because tcp connection to a service's target failed.) <P>
423 Causes the record to be flushed from the daemon's cache (as well as all other
424 daemons' caches on the network) if the record is determined to be invalid.<P>
426 Currently unused, reserved for future use.
429 If non-zero, specifies the interface on which to reconfirm the record
430 (the index for a given interface is determined via the if_nametoindex()
431 family of calls.) Passing 0 causes the name to be reconfirmed on all
432 interfaces. Passing -1 causes the name to be reconfirmed only on the
436 The resource record's full domain name.
439 The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
442 The class of the resource record, as defined in nameser.h (usually 1).
445 The raw rdata of the resource record.
447 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
448 @see RuntimePermission
450 public static void reconfirmRecord( int flags
, int ifIndex
, String fullName
, int rrtype
,
451 int rrclass
, byte[] rdata
)
452 { getInstance()._reconfirmRecord( flags
, ifIndex
, fullName
, rrtype
, rrclass
, rdata
); }
454 /** Return the canonical name of a particular interface index.<P>
456 A valid interface index. Must not be ALL_INTERFACES.
458 @return The name of the interface, which should match java.net.NetworkInterface.getName().
460 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
461 @see RuntimePermission
463 public static String
getNameForIfIndex( int ifIndex
)
464 { return getInstance()._getNameForIfIndex( ifIndex
); }
466 /** Return the index of a named interface.<P>
468 A valid interface name. An example is java.net.NetworkInterface.getName().
470 @return The interface index.
472 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
473 @see RuntimePermission
475 public static int getIfIndexForName( String ifName
)
476 { return getInstance()._getIfIndexForName( ifName
); }
478 protected DNSSD() {} // prevent direct instantiation
480 /** Return the single instance of DNSSD. */
481 static protected final DNSSD
getInstance()
483 SecurityManager sm
= System
.getSecurityManager();
485 sm
.checkPermission( new RuntimePermission( "getDNSSDInstance"));
489 abstract protected DNSSDService
_makeBrowser( int flags
, int ifIndex
, String regType
, String domain
, BrowseListener listener
)
490 throws DNSSDException
;
492 abstract protected DNSSDService
_resolve( int flags
, int ifIndex
, String serviceName
, String regType
,
493 String domain
, ResolveListener listener
)
494 throws DNSSDException
;
496 abstract protected DNSSDRegistration
_register( int flags
, int ifIndex
, String serviceName
, String regType
,
497 String domain
, String host
, int port
, TXTRecord txtRecord
, RegisterListener listener
)
498 throws DNSSDException
;
500 abstract protected DNSSDRecordRegistrar
_createRecordRegistrar( RegisterRecordListener listener
)
501 throws DNSSDException
;
503 abstract protected DNSSDService
_queryRecord( int flags
, int ifIndex
, String serviceName
, int rrtype
,
504 int rrclass
, QueryListener listener
)
505 throws DNSSDException
;
507 abstract protected DNSSDService
_enumerateDomains( int flags
, int ifIndex
, DomainListener listener
)
508 throws DNSSDException
;
510 abstract protected String
_constructFullName( String serviceName
, String regType
, String domain
)
511 throws DNSSDException
;
513 abstract protected void _reconfirmRecord( int flags
, int ifIndex
, String fullName
, int rrtype
,
514 int rrclass
, byte[] rdata
);
516 abstract protected String
_getNameForIfIndex( int ifIndex
);
518 abstract protected int _getIfIndexForName( String ifName
);
520 protected static DNSSD fInstance
;
526 String name
= System
.getProperty( "com.apple.dnssd.DNSSD" );
528 name
= "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class.
529 fInstance
= (DNSSD
) Class
.forName( name
).newInstance();
533 throw new InternalError( "cannot instantiate DNSSD" + e
);
539 // Concrete implementation of DNSSDException
540 class AppleDNSSDException
extends DNSSDException
542 public AppleDNSSDException( int errorCode
) { fErrorCode
= errorCode
; }
544 public int getErrorCode() { return fErrorCode
; }
546 public String
getMessage()
548 final String kMessages
[] = { // should probably be put into a resource or something
559 "ALREADY_REGISTERED",
564 "BAD_INTERFACE_INDEX",
576 "NATPORTMAPPINGUNSUPPORTED",
577 "NATPORTMAPPINGDISABLED"
580 if ( fErrorCode
<= UNKNOWN
&& fErrorCode
> ( UNKNOWN
- kMessages
.length
))
582 return "DNS-SD Error " + String
.valueOf( fErrorCode
) + ": " + kMessages
[ UNKNOWN
- fErrorCode
];
585 return super.getMessage() + "(" + String
.valueOf( fErrorCode
) + ")";
588 protected int fErrorCode
;
591 // The concrete, default implementation.
592 class AppleDNSSD
extends DNSSD
596 System
.loadLibrary( "jdns_sd");
598 int libInitResult
= InitLibrary( 2); // Current version number (must be sync'd with jnilib version)
600 if ( libInitResult
!= DNSSDException
.NO_ERROR
)
601 throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult
).getMessage());
604 static public boolean hasAutoCallbacks
; // Set by InitLibrary() to value of AUTO_CALLBACKS
606 protected DNSSDService
_makeBrowser( int flags
, int ifIndex
, String regType
, String domain
, BrowseListener client
)
607 throws DNSSDException
609 return new AppleBrowser( flags
, ifIndex
, regType
, domain
, client
);
612 protected DNSSDService
_resolve( int flags
, int ifIndex
, String serviceName
, String regType
,
613 String domain
, ResolveListener client
)
614 throws DNSSDException
616 return new AppleResolver( flags
, ifIndex
, serviceName
, regType
, domain
, client
);
619 protected DNSSDRegistration
_register( int flags
, int ifIndex
, String serviceName
, String regType
,
620 String domain
, String host
, int port
, TXTRecord txtRecord
, RegisterListener client
)
621 throws DNSSDException
623 return new AppleRegistration( flags
, ifIndex
, serviceName
, regType
, domain
, host
, port
,
624 ( txtRecord
!= null) ? txtRecord
.getRawBytes() : null, client
);
627 protected DNSSDRecordRegistrar
_createRecordRegistrar( RegisterRecordListener listener
)
628 throws DNSSDException
630 return new AppleRecordRegistrar( listener
);
633 protected DNSSDService
_queryRecord( int flags
, int ifIndex
, String serviceName
, int rrtype
,
634 int rrclass
, QueryListener client
)
635 throws DNSSDException
637 return new AppleQuery( flags
, ifIndex
, serviceName
, rrtype
, rrclass
, client
);
640 protected DNSSDService
_enumerateDomains( int flags
, int ifIndex
, DomainListener listener
)
641 throws DNSSDException
643 return new AppleDomainEnum( flags
, ifIndex
, listener
);
646 protected String
_constructFullName( String serviceName
, String regType
, String domain
)
647 throws DNSSDException
649 String
[] responseHolder
= new String
[1]; // lame maneuver to get around Java's lack of reference parameters
651 int rc
= ConstructName( serviceName
, regType
, domain
, responseHolder
);
653 throw new AppleDNSSDException( rc
);
655 return responseHolder
[0];
658 protected void _reconfirmRecord( int flags
, int ifIndex
, String fullName
, int rrtype
,
659 int rrclass
, byte[] rdata
)
661 ReconfirmRecord( flags
, ifIndex
, fullName
, rrtype
, rrclass
, rdata
);
664 protected String
_getNameForIfIndex( int ifIndex
)
666 return GetNameForIfIndex( ifIndex
);
669 protected int _getIfIndexForName( String ifName
)
671 return GetIfIndexForName( ifName
);
675 protected native int ConstructName( String serviceName
, String regType
, String domain
, String
[] pOut
);
677 protected native void ReconfirmRecord( int flags
, int ifIndex
, String fullName
, int rrtype
,
678 int rrclass
, byte[] rdata
);
680 protected native String
GetNameForIfIndex( int ifIndex
);
682 protected native int GetIfIndexForName( String ifName
);
684 protected static native int InitLibrary( int callerVersion
);
687 class AppleService
implements DNSSDService
, Runnable
689 public AppleService(BaseListener listener
) { fNativeContext
= 0; fListener
= listener
; }
691 public void stop() { this.HaltOperation(); }
693 /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
694 protected native int BlockForData();
696 /* Call ProcessResults when data appears on socket descriptor. */
697 protected native int ProcessResults();
699 protected synchronized native void HaltOperation();
701 protected void ThrowOnErr( int rc
) throws DNSSDException
704 throw new AppleDNSSDException( rc
);
707 protected long /* warning */ fNativeContext
; // Private storage for native side
713 // Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to
714 // block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized
715 // we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread,
716 // and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD
717 // operation. The Unix kernel always allocates the lowest available file descriptor to a new socket,
718 // so the same file descriptor is highly likely to be reused for the new operation, and if our old
719 // stale ServiceThread accidentally consumes bytes off that new socket we'll get really messed up.
720 // To guard against that, before calling ProcessResults we check to ensure that our
721 // fNativeContext has not been deleted, which is a telltale sign that our operation was stopped.
722 // After calling ProcessResults we check again, because it's extremely common for callback
723 // functions to stop their own operation and start others. For example, a resolveListener callback
724 // may well stop the resolve and then start a QueryRecord call to monitor the TXT record.
726 // The remaining risk is that between our checking fNativeContext and calling ProcessResults(),
727 // some other thread could stop the operation and start a new one using same file descriptor, and
728 // we wouldn't know. To prevent this, the AppleService object's HaltOperation() routine is declared
729 // synchronized and we perform our checks synchronized on the AppleService object, which ensures
730 // that HaltOperation() can't execute while we're doing it. Because Java locks are re-entrant this
731 // locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent
732 // any other thread from stopping it until after the callback has completed and returned to us here.
734 int result
= BlockForData();
737 if (fNativeContext
== 0) break; // Some other thread stopped our DNSSD operation; time to terminate this thread
738 if (result
== 0) continue; // If BlockForData() said there was no data, go back and block again
739 result
= ProcessResults();
740 if (fNativeContext
== 0) break; // Event listener stopped its own DNSSD operation; terminate this thread
741 if (result
!= 0) { fListener
.operationFailed(this, result
); break; } // If error, notify listener
746 protected BaseListener fListener
;
750 class AppleBrowser
extends AppleService
752 public AppleBrowser( int flags
, int ifIndex
, String regType
, String domain
, BrowseListener client
)
753 throws DNSSDException
756 this.ThrowOnErr( this.CreateBrowser( flags
, ifIndex
, regType
, domain
));
757 if ( !AppleDNSSD
.hasAutoCallbacks
)
758 new Thread(this).start();
761 // Sets fNativeContext. Returns non-zero on error.
762 protected native int CreateBrowser( int flags
, int ifIndex
, String regType
, String domain
);
765 class AppleResolver
extends AppleService
767 public AppleResolver( int flags
, int ifIndex
, String serviceName
, String regType
,
768 String domain
, ResolveListener client
)
769 throws DNSSDException
772 this.ThrowOnErr( this.CreateResolver( flags
, ifIndex
, serviceName
, regType
, domain
));
773 if ( !AppleDNSSD
.hasAutoCallbacks
)
774 new Thread(this).start();
777 // Sets fNativeContext. Returns non-zero on error.
778 protected native int CreateResolver( int flags
, int ifIndex
, String serviceName
, String regType
,
782 // An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord.
783 class AppleDNSRecord
implements DNSRecord
785 public AppleDNSRecord( AppleService owner
)
788 fRecord
= 0; // record always starts out empty
791 public void update( int flags
, byte[] rData
, int ttl
)
792 throws DNSSDException
794 this.ThrowOnErr( this.Update( flags
, rData
, ttl
));
798 throws DNSSDException
800 this.ThrowOnErr( this.Remove());
803 protected long fRecord
; // Really a DNSRecord; sizeof(long) == sizeof(void*) ?
804 protected AppleService fOwner
;
806 protected void ThrowOnErr( int rc
) throws DNSSDException
809 throw new AppleDNSSDException( rc
);
812 protected native int Update( int flags
, byte[] rData
, int ttl
);
814 protected native int Remove();
817 class AppleRegistration
extends AppleService
implements DNSSDRegistration
819 public AppleRegistration( int flags
, int ifIndex
, String serviceName
, String regType
, String domain
,
820 String host
, int port
, byte[] txtRecord
, RegisterListener client
)
821 throws DNSSDException
824 this.ThrowOnErr( this.BeginRegister( ifIndex
, flags
, serviceName
, regType
, domain
, host
, port
, txtRecord
));
825 if ( !AppleDNSSD
.hasAutoCallbacks
)
826 new Thread(this).start();
829 public DNSRecord
addRecord( int flags
, int rrType
, byte[] rData
, int ttl
)
830 throws DNSSDException
832 AppleDNSRecord newRecord
= new AppleDNSRecord( this);
834 this.ThrowOnErr( this.AddRecord( flags
, rrType
, rData
, ttl
, newRecord
));
838 public DNSRecord
getTXTRecord()
839 throws DNSSDException
841 return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record
844 // Sets fNativeContext. Returns non-zero on error.
845 protected native int BeginRegister( int ifIndex
, int flags
, String serviceName
, String regType
,
846 String domain
, String host
, int port
, byte[] txtRecord
);
848 // Sets fNativeContext. Returns non-zero on error.
849 protected native int AddRecord( int flags
, int rrType
, byte[] rData
, int ttl
, AppleDNSRecord destObj
);
852 class AppleRecordRegistrar
extends AppleService
implements DNSSDRecordRegistrar
854 public AppleRecordRegistrar( RegisterRecordListener listener
)
855 throws DNSSDException
858 this.ThrowOnErr( this.CreateConnection());
859 if ( !AppleDNSSD
.hasAutoCallbacks
)
860 new Thread(this).start();
863 public DNSRecord
registerRecord( int flags
, int ifIndex
, String fullname
, int rrtype
,
864 int rrclass
, byte[] rdata
, int ttl
)
865 throws DNSSDException
867 AppleDNSRecord newRecord
= new AppleDNSRecord( this);
869 this.ThrowOnErr( this.RegisterRecord( flags
, ifIndex
, fullname
, rrtype
, rrclass
, rdata
, ttl
, newRecord
));
873 // Sets fNativeContext. Returns non-zero on error.
874 protected native int CreateConnection();
876 // Sets fNativeContext. Returns non-zero on error.
877 protected native int RegisterRecord( int flags
, int ifIndex
, String fullname
, int rrtype
,
878 int rrclass
, byte[] rdata
, int ttl
, AppleDNSRecord destObj
);
881 class AppleQuery
extends AppleService
883 public AppleQuery( int flags
, int ifIndex
, String serviceName
, int rrtype
,
884 int rrclass
, QueryListener client
)
885 throws DNSSDException
888 this.ThrowOnErr( this.CreateQuery( flags
, ifIndex
, serviceName
, rrtype
, rrclass
));
889 if ( !AppleDNSSD
.hasAutoCallbacks
)
890 new Thread(this).start();
893 // Sets fNativeContext. Returns non-zero on error.
894 protected native int CreateQuery( int flags
, int ifIndex
, String serviceName
, int rrtype
, int rrclass
);
897 class AppleDomainEnum
extends AppleService
899 public AppleDomainEnum( int flags
, int ifIndex
, DomainListener client
)
900 throws DNSSDException
903 this.ThrowOnErr( this.BeginEnum( flags
, ifIndex
));
904 if ( !AppleDNSSD
.hasAutoCallbacks
)
905 new Thread(this).start();
908 // Sets fNativeContext. Returns non-zero on error.
909 protected native int BeginEnum( int flags
, int ifIndex
);