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.15 2007/03/13 00:28:03 vazquez
21 <rdar://problem/4625928> Java: Rename exported symbols in libjdns_sd.jnilib
23 Revision 1.14 2007/03/13 00:10:14 vazquez
24 <rdar://problem/4455206> Java: 64 bit JNI patch
26 Revision 1.13 2007/02/24 23:08:02 mkrochma
27 <rdar://problem/5001673> Typo in Bonjour Java API document
29 Revision 1.12 2007/02/09 00:33:02 cheshire
30 Add missing error codes to kMessages array
32 Revision 1.11 2006/08/14 23:25:08 cheshire
33 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
35 Revision 1.10 2006/06/20 23:05:55 rpantos
36 <rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent
38 Revision 1.9 2005/10/26 01:52:24 cheshire
39 <rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux)
41 Revision 1.8 2005/07/11 01:55:21 cheshire
42 <rdar://problem/4175511> Race condition in Java API
44 Revision 1.7 2005/07/05 13:01:52 cheshire
45 <rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time
47 Revision 1.6 2005/07/05 00:02:25 cheshire
50 Revision 1.5 2005/07/04 21:13:47 cheshire
51 Add missing error message strings
53 Revision 1.4 2004/12/11 03:00:59 rpantos
54 <rdar://problem/3907498> Java DNSRecord API should be cleaned up
56 Revision 1.3 2004/11/12 03:23:08 rpantos
57 rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex.
59 Revision 1.2 2004/05/20 17:43:18 cheshire
60 Fix invalid UTF-8 characters in file
62 Revision 1.1 2004/04/30 16:32:34 rpantos
66 This file declares and implements DNSSD, the central Java factory class
67 for doing DNS Service Discovery. It includes the mostly-abstract public
68 interface, as well as the Apple* implementation subclasses.
72 package com
.apple
.dnssd
;
76 DNSSD provides access to DNS Service Discovery features of ZeroConf networking.<P>
78 It is a factory class that is used to invoke registration and discovery-related
79 operations. Most operations are non-blocking; clients are called back through an interface
80 with the result of an operation. Callbacks are made from a separate worker thread.<P>
82 For example, in this program<P>
84 class MyClient implements BrowseListener {
85 void lookForWebServers() {
86 myBrowser = DNSSD.browse("_http._tcp", this);
89 public void serviceFound(DNSSDService browser, int flags, int ifIndex,
90 String serviceName, String regType, String domain) {}
93 <CODE>MyClient.serviceFound()</CODE> would be called for every HTTP server discovered in the
94 default browse domain(s).
97 abstract public class DNSSD
99 /** Flag indicates to a {@link BrowseListener} that another result is
100 queued. Applications should not update their UI to display browse
101 results if the MORE_COMING flag is set; they will be called at least once
102 more with the flag clear.
104 public static final int MORE_COMING
= ( 1 << 0 );
106 /** If flag is set in a {@link DomainListener} callback, indicates that the result is the default domain. */
107 public static final int DEFAULT
= ( 1 << 2 );
109 /** If flag is set, a name conflict will trigger an exception when registering non-shared records.<P>
110 A name must be explicitly specified when registering a service if this bit is set
111 (i.e. the default name may not not be used).
113 public static final int NO_AUTO_RENAME
= ( 1 << 3 );
115 /** If flag is set, allow multiple records with this name on the network (e.g. PTR records)
116 when registering individual records on a {@link DNSSDRegistration}.
118 public static final int SHARED
= ( 1 << 4 );
120 /** If flag is set, records with this name must be unique on the network (e.g. SRV records). */
121 public static final int UNIQUE
= ( 1 << 5 );
123 /** Set flag when calling enumerateDomains() to restrict results to domains recommended for browsing. */
124 public static final int BROWSE_DOMAINS
= ( 1 << 6 );
125 /** Set flag when calling enumerateDomains() to restrict results to domains recommended for registration. */
126 public static final int REGISTRATION_DOMAINS
= ( 1 << 7 );
128 /** Maximum length, in bytes, of a domain name represented as an escaped C-String. */
129 public static final int MAX_DOMAIN_NAME
= 1005;
131 /** Pass for ifIndex to specify all available interfaces. */
132 public static final int ALL_INTERFACES
= 0;
134 /** Pass for ifIndex to specify the localhost interface. */
135 public static final int LOCALHOST_ONLY
= -1;
137 /** Browse for instances of a service.<P>
139 Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P>
142 Currently ignored, reserved for future use.
145 If non-zero, specifies the interface on which to browse for services
146 (the index for a given interface is determined via the if_nametoindex()
147 family of calls.) Most applications will pass 0 to browse on all available
148 interfaces. Pass -1 to only browse for services provided on the local host.
151 The registration type being browsed for followed by the protocol, separated by a
152 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
155 If non-null, specifies the domain on which to browse for services.
156 Most applications will not specify a domain, instead browsing on the
160 This object will get called when instances of the service are discovered (or disappear).
162 @return A {@link DNSSDService} that represents the active browse operation.
164 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
165 @see RuntimePermission
167 public static DNSSDService
browse( int flags
, int ifIndex
, String regType
, String domain
, BrowseListener listener
)
168 throws DNSSDException
169 { return getInstance()._makeBrowser( flags
, ifIndex
, regType
, domain
, listener
); }
171 /** Browse for instances of a service. Use default flags, ifIndex and domain.<P>
174 The registration type being browsed for followed by the protocol, separated by a
175 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
178 This object will get called when instances of the service are discovered (or disappear).
180 @return A {@link DNSSDService} that represents the active browse operation.
182 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
183 @see RuntimePermission
185 public static DNSSDService
browse( String regType
, BrowseListener listener
)
186 throws DNSSDException
187 { return browse( 0, 0, regType
, "", listener
); }
189 /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.<P>
191 Note: Applications should NOT use resolve() solely for txt record monitoring - use
192 queryRecord() instead, as it is more efficient for this task.<P>
194 Note: When the desired results have been returned, the client MUST terminate the resolve by
195 calling {@link DNSSDService#stop}.<P>
197 Note: resolve() behaves correctly for typical services that have a single SRV record and
198 a single TXT record (the TXT record may be empty.) To resolve non-standard services with
199 multiple SRV or TXT records, use queryRecord().<P>
202 Currently ignored, reserved for future use.
205 The interface on which to resolve the service. The client should
206 pass the interface on which the serviceName was discovered (i.e.
207 the ifIndex passed to the serviceFound() callback)
208 or 0 to resolve the named service on all available interfaces.
211 The servicename to be resolved.
214 The registration type being resolved followed by the protocol, separated by a
215 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
218 The domain on which the service is registered, i.e. the domain passed
219 to the serviceFound() callback.
222 This object will get called when the service is resolved.
224 @return A {@link DNSSDService} that represents the active resolve operation.
226 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
227 @see RuntimePermission
229 public static DNSSDService
resolve( int flags
, int ifIndex
, String serviceName
, String regType
,
230 String domain
, ResolveListener listener
)
231 throws DNSSDException
232 { return getInstance()._resolve( flags
, ifIndex
, serviceName
, regType
, domain
, listener
); }
234 /** Register a service, to be discovered via browse() and resolve() calls.<P>
236 Possible values are: NO_AUTO_RENAME.
239 If non-zero, specifies the interface on which to register the service
240 (the index for a given interface is determined via the if_nametoindex()
241 family of calls.) Most applications will pass 0 to register on all
242 available interfaces. Pass -1 to register a service only on the local
243 machine (service will not be visible to remote hosts).
246 If non-null, specifies the service name to be registered.
247 Applications need not specify a name, in which case the
248 computer name is used (this name is communicated to the client via
249 the serviceRegistered() callback).
252 The registration type being registered followed by the protocol, separated by a
253 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
256 If non-null, specifies the domain on which to advertise the service.
257 Most applications will not specify a domain, instead automatically
258 registering in the default domain(s).
261 If non-null, specifies the SRV target host name. Most applications
262 will not specify a host, instead automatically using the machine's
263 default host name(s). Note that specifying a non-null host does NOT
264 create an address record for that host - the application is responsible
265 for ensuring that the appropriate address record exists, or creating it
266 via {@link DNSSDRegistration#addRecord}.
269 The port on which the service accepts connections. Pass 0 for a
270 "placeholder" service (i.e. a service that will not be discovered by
271 browsing, but will cause a name conflict if another client tries to
272 register that same name.) Most clients will not use placeholder services.
275 The txt record rdata. May be null. Note that a non-null txtRecord
276 MUST be a properly formatted DNS TXT record, i.e. <length byte> <data>
277 <length byte> <data> ...
280 This object will get called when the service is registered.
282 @return A {@link DNSSDRegistration} that controls the active registration.
284 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
285 @see RuntimePermission
287 public static DNSSDRegistration
register( int flags
, int ifIndex
, String serviceName
, String regType
,
288 String domain
, String host
, int port
, TXTRecord txtRecord
, RegisterListener listener
)
289 throws DNSSDException
290 { return getInstance()._register( flags
, ifIndex
, serviceName
, regType
, domain
, host
, port
, txtRecord
, listener
); }
292 /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.<P>
294 If non-null, specifies the service name to be registered.
295 Applications need not specify a name, in which case the
296 computer name is used (this name is communicated to the client via
297 the serviceRegistered() callback).
300 The registration type being registered followed by the protocol, separated by a
301 dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
304 The port on which the service accepts connections. Pass 0 for a
305 "placeholder" service (i.e. a service that will not be discovered by
306 browsing, but will cause a name conflict if another client tries to
307 register that same name.) Most clients will not use placeholder services.
310 This object will get called when the service is registered.
312 @return A {@link DNSSDRegistration} that controls the active registration.
314 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
315 @see RuntimePermission
317 public static DNSSDRegistration
register( String serviceName
, String regType
, int port
, RegisterListener listener
)
318 throws DNSSDException
319 { return register( 0, 0, serviceName
, regType
, null, null, port
, null, listener
); }
321 /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of
322 multiple individual records.<P>
324 @return A {@link DNSSDRecordRegistrar} that can be used to register records.
326 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
327 @see RuntimePermission
329 public static DNSSDRecordRegistrar
createRecordRegistrar( RegisterRecordListener listener
)
330 throws DNSSDException
331 { return getInstance()._createRecordRegistrar( listener
); }
333 /** Query for an arbitrary DNS record.<P>
335 Possible values are: MORE_COMING.
338 If non-zero, specifies the interface on which to issue the query
339 (the index for a given interface is determined via the if_nametoindex()
340 family of calls.) Passing 0 causes the name to be queried for on all
341 interfaces. Passing -1 causes the name to be queried for only on the
345 The full domain name of the resource record to be queried for.
348 The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
349 as defined in nameser.h.
352 The class of the resource record, as defined in nameser.h
353 (usually 1 for the Internet class).
356 This object will get called when the query completes.
358 @return A {@link DNSSDService} that controls the active query.
360 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
361 @see RuntimePermission
363 public static DNSSDService
queryRecord( int flags
, int ifIndex
, String serviceName
, int rrtype
,
364 int rrclass
, QueryListener listener
)
365 throws DNSSDException
366 { return getInstance()._queryRecord( flags
, ifIndex
, serviceName
, rrtype
, rrclass
, listener
); }
368 /** Asynchronously enumerate domains available for browsing and registration.<P>
370 Currently, the only domain returned is "local.", but other domains will be returned in future.<P>
372 The enumeration MUST be cancelled by calling {@link DNSSDService#stop} when no more domains
375 Possible values are: BROWSE_DOMAINS, REGISTRATION_DOMAINS.
378 If non-zero, specifies the interface on which to look for domains.
379 (the index for a given interface is determined via the if_nametoindex()
380 family of calls.) Most applications will pass 0 to enumerate domains on
384 This object will get called when domains are found.
386 @return A {@link DNSSDService} that controls the active enumeration.
388 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
389 @see RuntimePermission
391 public static DNSSDService
enumerateDomains( int flags
, int ifIndex
, DomainListener listener
)
392 throws DNSSDException
393 { return getInstance()._enumerateDomains( flags
, ifIndex
, listener
); }
395 /** Concatenate a three-part domain name (as provided to the listeners) into a
396 properly-escaped full domain name. Note that strings passed to listeners are
397 ALREADY ESCAPED where necessary.<P>
399 The service name - any dots or slashes must NOT be escaped.
400 May be null (to construct a PTR record name, e.g. "_ftp._tcp.apple.com").
403 The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").
406 The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped.
408 @return The full domain name.
410 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
411 @see RuntimePermission
413 public static String
constructFullName( String serviceName
, String regType
, String domain
)
414 throws DNSSDException
415 { return getInstance()._constructFullName( serviceName
, regType
, domain
); }
417 /** Instruct the daemon to verify the validity of a resource record that appears to
418 be out of date. (e.g. because tcp connection to a service's target failed.) <P>
420 Causes the record to be flushed from the daemon's cache (as well as all other
421 daemons' caches on the network) if the record is determined to be invalid.<P>
423 Currently unused, reserved for future use.
426 If non-zero, specifies the interface on which to reconfirm the record
427 (the index for a given interface is determined via the if_nametoindex()
428 family of calls.) Passing 0 causes the name to be reconfirmed on all
429 interfaces. Passing -1 causes the name to be reconfirmed only on the
433 The resource record's full domain name.
436 The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
439 The class of the resource record, as defined in nameser.h (usually 1).
442 The raw rdata of the resource record.
444 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
445 @see RuntimePermission
447 public static void reconfirmRecord( int flags
, int ifIndex
, String fullName
, int rrtype
,
448 int rrclass
, byte[] rdata
)
449 { getInstance()._reconfirmRecord( flags
, ifIndex
, fullName
, rrtype
, rrclass
, rdata
); }
451 /** Return the canonical name of a particular interface index.<P>
453 A valid interface index. Must not be ALL_INTERFACES.
455 @return The name of the interface, which should match java.net.NetworkInterface.getName().
457 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
458 @see RuntimePermission
460 public static String
getNameForIfIndex( int ifIndex
)
461 { return getInstance()._getNameForIfIndex( ifIndex
); }
463 /** Return the index of a named interface.<P>
465 A valid interface name. An example is java.net.NetworkInterface.getName().
467 @return The interface index.
469 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
470 @see RuntimePermission
472 public static int getIfIndexForName( String ifName
)
473 { return getInstance()._getIfIndexForName( ifName
); }
475 protected DNSSD() {} // prevent direct instantiation
477 /** Return the single instance of DNSSD. */
478 static protected final DNSSD
getInstance()
480 SecurityManager sm
= System
.getSecurityManager();
482 sm
.checkPermission( new RuntimePermission( "getDNSSDInstance"));
486 abstract protected DNSSDService
_makeBrowser( int flags
, int ifIndex
, String regType
, String domain
, BrowseListener listener
)
487 throws DNSSDException
;
489 abstract protected DNSSDService
_resolve( int flags
, int ifIndex
, String serviceName
, String regType
,
490 String domain
, ResolveListener listener
)
491 throws DNSSDException
;
493 abstract protected DNSSDRegistration
_register( int flags
, int ifIndex
, String serviceName
, String regType
,
494 String domain
, String host
, int port
, TXTRecord txtRecord
, RegisterListener listener
)
495 throws DNSSDException
;
497 abstract protected DNSSDRecordRegistrar
_createRecordRegistrar( RegisterRecordListener listener
)
498 throws DNSSDException
;
500 abstract protected DNSSDService
_queryRecord( int flags
, int ifIndex
, String serviceName
, int rrtype
,
501 int rrclass
, QueryListener listener
)
502 throws DNSSDException
;
504 abstract protected DNSSDService
_enumerateDomains( int flags
, int ifIndex
, DomainListener listener
)
505 throws DNSSDException
;
507 abstract protected String
_constructFullName( String serviceName
, String regType
, String domain
)
508 throws DNSSDException
;
510 abstract protected void _reconfirmRecord( int flags
, int ifIndex
, String fullName
, int rrtype
,
511 int rrclass
, byte[] rdata
);
513 abstract protected String
_getNameForIfIndex( int ifIndex
);
515 abstract protected int _getIfIndexForName( String ifName
);
517 protected static DNSSD fInstance
;
523 String name
= System
.getProperty( "com.apple.dnssd.DNSSD" );
525 name
= "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class.
526 fInstance
= (DNSSD
) Class
.forName( name
).newInstance();
530 throw new InternalError( "cannot instantiate DNSSD" + e
);
536 // Concrete implementation of DNSSDException
537 class AppleDNSSDException
extends DNSSDException
539 public AppleDNSSDException( int errorCode
) { fErrorCode
= errorCode
; }
541 public int getErrorCode() { return fErrorCode
; }
543 public String
getMessage()
545 final String kMessages
[] = { // should probably be put into a resource or something
556 "ALREADY_REGISTERED",
561 "BAD_INTERFACE_INDEX",
573 "NATPORTMAPPINGUNSUPPORTED",
574 "NATPORTMAPPINGDISABLED"
577 if ( fErrorCode
<= UNKNOWN
&& fErrorCode
> ( UNKNOWN
- kMessages
.length
))
579 return "DNS-SD Error " + String
.valueOf( fErrorCode
) + ": " + kMessages
[ UNKNOWN
- fErrorCode
];
582 return super.getMessage() + "(" + String
.valueOf( fErrorCode
) + ")";
585 protected int fErrorCode
;
588 // The concrete, default implementation.
589 class AppleDNSSD
extends DNSSD
593 System
.loadLibrary( "jdns_sd");
595 int libInitResult
= InitLibrary( 2); // Current version number (must be sync'd with jnilib version)
597 if ( libInitResult
!= DNSSDException
.NO_ERROR
)
598 throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult
).getMessage());
601 static public boolean hasAutoCallbacks
; // Set by InitLibrary() to value of AUTO_CALLBACKS
603 protected DNSSDService
_makeBrowser( int flags
, int ifIndex
, String regType
, String domain
, BrowseListener client
)
604 throws DNSSDException
606 return new AppleBrowser( flags
, ifIndex
, regType
, domain
, client
);
609 protected DNSSDService
_resolve( int flags
, int ifIndex
, String serviceName
, String regType
,
610 String domain
, ResolveListener client
)
611 throws DNSSDException
613 return new AppleResolver( flags
, ifIndex
, serviceName
, regType
, domain
, client
);
616 protected DNSSDRegistration
_register( int flags
, int ifIndex
, String serviceName
, String regType
,
617 String domain
, String host
, int port
, TXTRecord txtRecord
, RegisterListener client
)
618 throws DNSSDException
620 return new AppleRegistration( flags
, ifIndex
, serviceName
, regType
, domain
, host
, port
,
621 ( txtRecord
!= null) ? txtRecord
.getRawBytes() : null, client
);
624 protected DNSSDRecordRegistrar
_createRecordRegistrar( RegisterRecordListener listener
)
625 throws DNSSDException
627 return new AppleRecordRegistrar( listener
);
630 protected DNSSDService
_queryRecord( int flags
, int ifIndex
, String serviceName
, int rrtype
,
631 int rrclass
, QueryListener client
)
632 throws DNSSDException
634 return new AppleQuery( flags
, ifIndex
, serviceName
, rrtype
, rrclass
, client
);
637 protected DNSSDService
_enumerateDomains( int flags
, int ifIndex
, DomainListener listener
)
638 throws DNSSDException
640 return new AppleDomainEnum( flags
, ifIndex
, listener
);
643 protected String
_constructFullName( String serviceName
, String regType
, String domain
)
644 throws DNSSDException
646 String
[] responseHolder
= new String
[1]; // lame maneuver to get around Java's lack of reference parameters
648 int rc
= ConstructName( serviceName
, regType
, domain
, responseHolder
);
650 throw new AppleDNSSDException( rc
);
652 return responseHolder
[0];
655 protected void _reconfirmRecord( int flags
, int ifIndex
, String fullName
, int rrtype
,
656 int rrclass
, byte[] rdata
)
658 ReconfirmRecord( flags
, ifIndex
, fullName
, rrtype
, rrclass
, rdata
);
661 protected String
_getNameForIfIndex( int ifIndex
)
663 return GetNameForIfIndex( ifIndex
);
666 protected int _getIfIndexForName( String ifName
)
668 return GetIfIndexForName( ifName
);
672 protected native int ConstructName( String serviceName
, String regType
, String domain
, String
[] pOut
);
674 protected native void ReconfirmRecord( int flags
, int ifIndex
, String fullName
, int rrtype
,
675 int rrclass
, byte[] rdata
);
677 protected native String
GetNameForIfIndex( int ifIndex
);
679 protected native int GetIfIndexForName( String ifName
);
681 protected static native int InitLibrary( int callerVersion
);
684 class AppleService
implements DNSSDService
, Runnable
686 public AppleService(BaseListener listener
) { fNativeContext
= 0; fListener
= listener
; }
688 public void stop() { this.HaltOperation(); }
690 /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
691 protected native int BlockForData();
693 /* Call ProcessResults when data appears on socket descriptor. */
694 protected native int ProcessResults();
696 protected synchronized native void HaltOperation();
698 protected void ThrowOnErr( int rc
) throws DNSSDException
701 throw new AppleDNSSDException( rc
);
704 protected long /* warning */ fNativeContext
; // Private storage for native side
710 // Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to
711 // block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized
712 // we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread,
713 // and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD
714 // operation. The Unix kernel always allocates the lowest available file descriptor to a new socket,
715 // so the same file descriptor is highly likely to be reused for the new operation, and if our old
716 // stale ServiceThread accidentally consumes bytes off that new socket we'll get really messed up.
717 // To guard against that, before calling ProcessResults we check to ensure that our
718 // fNativeContext has not been deleted, which is a telltale sign that our operation was stopped.
719 // After calling ProcessResults we check again, because it's extremely common for callback
720 // functions to stop their own operation and start others. For example, a resolveListener callback
721 // may well stop the resolve and then start a QueryRecord call to monitor the TXT record.
723 // The remaining risk is that between our checking fNativeContext and calling ProcessResults(),
724 // some other thread could stop the operation and start a new one using same file descriptor, and
725 // we wouldn't know. To prevent this, the AppleService object's HaltOperation() routine is declared
726 // synchronized and we perform our checks synchronized on the AppleService object, which ensures
727 // that HaltOperation() can't execute while we're doing it. Because Java locks are re-entrant this
728 // locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent
729 // any other thread from stopping it until after the callback has completed and returned to us here.
731 int result
= BlockForData();
734 if (fNativeContext
== 0) break; // Some other thread stopped our DNSSD operation; time to terminate this thread
735 if (result
== 0) continue; // If BlockForData() said there was no data, go back and block again
736 result
= ProcessResults();
737 if (fNativeContext
== 0) break; // Event listener stopped its own DNSSD operation; terminate this thread
738 if (result
!= 0) { fListener
.operationFailed(this, result
); break; } // If error, notify listener
743 protected BaseListener fListener
;
747 class AppleBrowser
extends AppleService
749 public AppleBrowser( int flags
, int ifIndex
, String regType
, String domain
, BrowseListener client
)
750 throws DNSSDException
753 this.ThrowOnErr( this.CreateBrowser( flags
, ifIndex
, regType
, domain
));
754 if ( !AppleDNSSD
.hasAutoCallbacks
)
755 new Thread(this).start();
758 // Sets fNativeContext. Returns non-zero on error.
759 protected native int CreateBrowser( int flags
, int ifIndex
, String regType
, String domain
);
762 class AppleResolver
extends AppleService
764 public AppleResolver( int flags
, int ifIndex
, String serviceName
, String regType
,
765 String domain
, ResolveListener client
)
766 throws DNSSDException
769 this.ThrowOnErr( this.CreateResolver( flags
, ifIndex
, serviceName
, regType
, domain
));
770 if ( !AppleDNSSD
.hasAutoCallbacks
)
771 new Thread(this).start();
774 // Sets fNativeContext. Returns non-zero on error.
775 protected native int CreateResolver( int flags
, int ifIndex
, String serviceName
, String regType
,
779 // An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord.
780 class AppleDNSRecord
implements DNSRecord
782 public AppleDNSRecord( AppleService owner
)
785 fRecord
= 0; // record always starts out empty
788 public void update( int flags
, byte[] rData
, int ttl
)
789 throws DNSSDException
791 this.ThrowOnErr( this.Update( flags
, rData
, ttl
));
795 throws DNSSDException
797 this.ThrowOnErr( this.Remove());
800 protected long fRecord
; // Really a DNSRecord; sizeof(long) == sizeof(void*) ?
801 protected AppleService fOwner
;
803 protected void ThrowOnErr( int rc
) throws DNSSDException
806 throw new AppleDNSSDException( rc
);
809 protected native int Update( int flags
, byte[] rData
, int ttl
);
811 protected native int Remove();
814 class AppleRegistration
extends AppleService
implements DNSSDRegistration
816 public AppleRegistration( int flags
, int ifIndex
, String serviceName
, String regType
, String domain
,
817 String host
, int port
, byte[] txtRecord
, RegisterListener client
)
818 throws DNSSDException
821 this.ThrowOnErr( this.BeginRegister( ifIndex
, flags
, serviceName
, regType
, domain
, host
, port
, txtRecord
));
822 if ( !AppleDNSSD
.hasAutoCallbacks
)
823 new Thread(this).start();
826 public DNSRecord
addRecord( int flags
, int rrType
, byte[] rData
, int ttl
)
827 throws DNSSDException
829 AppleDNSRecord newRecord
= new AppleDNSRecord( this);
831 this.ThrowOnErr( this.AddRecord( flags
, rrType
, rData
, ttl
, newRecord
));
835 public DNSRecord
getTXTRecord()
836 throws DNSSDException
838 return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record
841 // Sets fNativeContext. Returns non-zero on error.
842 protected native int BeginRegister( int ifIndex
, int flags
, String serviceName
, String regType
,
843 String domain
, String host
, int port
, byte[] txtRecord
);
845 // Sets fNativeContext. Returns non-zero on error.
846 protected native int AddRecord( int flags
, int rrType
, byte[] rData
, int ttl
, AppleDNSRecord destObj
);
849 class AppleRecordRegistrar
extends AppleService
implements DNSSDRecordRegistrar
851 public AppleRecordRegistrar( RegisterRecordListener listener
)
852 throws DNSSDException
855 this.ThrowOnErr( this.CreateConnection());
856 if ( !AppleDNSSD
.hasAutoCallbacks
)
857 new Thread(this).start();
860 public DNSRecord
registerRecord( int flags
, int ifIndex
, String fullname
, int rrtype
,
861 int rrclass
, byte[] rdata
, int ttl
)
862 throws DNSSDException
864 AppleDNSRecord newRecord
= new AppleDNSRecord( this);
866 this.ThrowOnErr( this.RegisterRecord( flags
, ifIndex
, fullname
, rrtype
, rrclass
, rdata
, ttl
, newRecord
));
870 // Sets fNativeContext. Returns non-zero on error.
871 protected native int CreateConnection();
873 // Sets fNativeContext. Returns non-zero on error.
874 protected native int RegisterRecord( int flags
, int ifIndex
, String fullname
, int rrtype
,
875 int rrclass
, byte[] rdata
, int ttl
, AppleDNSRecord destObj
);
878 class AppleQuery
extends AppleService
880 public AppleQuery( int flags
, int ifIndex
, String serviceName
, int rrtype
,
881 int rrclass
, QueryListener client
)
882 throws DNSSDException
885 this.ThrowOnErr( this.CreateQuery( flags
, ifIndex
, serviceName
, rrtype
, rrclass
));
886 if ( !AppleDNSSD
.hasAutoCallbacks
)
887 new Thread(this).start();
890 // Sets fNativeContext. Returns non-zero on error.
891 protected native int CreateQuery( int flags
, int ifIndex
, String serviceName
, int rrtype
, int rrclass
);
894 class AppleDomainEnum
extends AppleService
896 public AppleDomainEnum( int flags
, int ifIndex
, DomainListener client
)
897 throws DNSSDException
900 this.ThrowOnErr( this.BeginEnum( flags
, ifIndex
));
901 if ( !AppleDNSSD
.hasAutoCallbacks
)
902 new Thread(this).start();
905 // Sets fNativeContext. Returns non-zero on error.
906 protected native int BeginEnum( int flags
, int ifIndex
);