]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSShared/Java/DNSSD.java
mDNSResponder-176.3.tar.gz
[apple/mdnsresponder.git] / mDNSShared / Java / DNSSD.java
1 /* -*- Mode: Java; tab-width: 4 -*-
2 *
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16
17 Change History (most recent first):
18
19 $Log: DNSSD.java,v $
20 Revision 1.15 2007/03/13 00:28:03 vazquez
21 <rdar://problem/4625928> Java: Rename exported symbols in libjdns_sd.jnilib
22
23 Revision 1.14 2007/03/13 00:10:14 vazquez
24 <rdar://problem/4455206> Java: 64 bit JNI patch
25
26 Revision 1.13 2007/02/24 23:08:02 mkrochma
27 <rdar://problem/5001673> Typo in Bonjour Java API document
28
29 Revision 1.12 2007/02/09 00:33:02 cheshire
30 Add missing error codes to kMessages array
31
32 Revision 1.11 2006/08/14 23:25:08 cheshire
33 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
34
35 Revision 1.10 2006/06/20 23:05:55 rpantos
36 <rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent
37
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)
40
41 Revision 1.8 2005/07/11 01:55:21 cheshire
42 <rdar://problem/4175511> Race condition in Java API
43
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
46
47 Revision 1.6 2005/07/05 00:02:25 cheshire
48 Add missing comma
49
50 Revision 1.5 2005/07/04 21:13:47 cheshire
51 Add missing error message strings
52
53 Revision 1.4 2004/12/11 03:00:59 rpantos
54 <rdar://problem/3907498> Java DNSRecord API should be cleaned up
55
56 Revision 1.3 2004/11/12 03:23:08 rpantos
57 rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex.
58
59 Revision 1.2 2004/05/20 17:43:18 cheshire
60 Fix invalid UTF-8 characters in file
61
62 Revision 1.1 2004/04/30 16:32:34 rpantos
63 First checked in.
64
65
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.
69 */
70
71
72 package com.apple.dnssd;
73
74
75 /**
76 DNSSD provides access to DNS Service Discovery features of ZeroConf networking.<P>
77
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>
81
82 For example, in this program<P>
83 <PRE><CODE>
84 class MyClient implements BrowseListener {
85 void lookForWebServers() {
86 myBrowser = DNSSD.browse("_http._tcp", this);
87 }
88
89 public void serviceFound(DNSSDService browser, int flags, int ifIndex,
90 String serviceName, String regType, String domain) {}
91 ...
92 }</CODE></PRE>
93 <CODE>MyClient.serviceFound()</CODE> would be called for every HTTP server discovered in the
94 default browse domain(s).
95 */
96
97 abstract public class DNSSD
98 {
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.
103 */
104 public static final int MORE_COMING = ( 1 << 0 );
105
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 );
108
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).
112 */
113 public static final int NO_AUTO_RENAME = ( 1 << 3 );
114
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}.
117 */
118 public static final int SHARED = ( 1 << 4 );
119
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 );
122
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 );
127
128 /** Maximum length, in bytes, of a domain name represented as an escaped C-String. */
129 public static final int MAX_DOMAIN_NAME = 1005;
130
131 /** Pass for ifIndex to specify all available interfaces. */
132 public static final int ALL_INTERFACES = 0;
133
134 /** Pass for ifIndex to specify the localhost interface. */
135 public static final int LOCALHOST_ONLY = -1;
136
137 /** Browse for instances of a service.<P>
138
139 Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P>
140
141 @param flags
142 Currently ignored, reserved for future use.
143 <P>
144 @param ifIndex
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.
149 <P>
150 @param regType
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".
153 <P>
154 @param domain
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
157 default domain(s).
158 <P>
159 @param listener
160 This object will get called when instances of the service are discovered (or disappear).
161 <P>
162 @return A {@link DNSSDService} that represents the active browse operation.
163
164 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
165 @see RuntimePermission
166 */
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); }
170
171 /** Browse for instances of a service. Use default flags, ifIndex and domain.<P>
172
173 @param regType
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".
176 <P>
177 @param listener
178 This object will get called when instances of the service are discovered (or disappear).
179 <P>
180 @return A {@link DNSSDService} that represents the active browse operation.
181
182 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
183 @see RuntimePermission
184 */
185 public static DNSSDService browse( String regType, BrowseListener listener)
186 throws DNSSDException
187 { return browse( 0, 0, regType, "", listener); }
188
189 /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.<P>
190
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>
193
194 Note: When the desired results have been returned, the client MUST terminate the resolve by
195 calling {@link DNSSDService#stop}.<P>
196
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>
200
201 @param flags
202 Currently ignored, reserved for future use.
203 <P>
204 @param ifIndex
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.
209 <P>
210 @param serviceName
211 The servicename to be resolved.
212 <P>
213 @param regType
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".
216 <P>
217 @param domain
218 The domain on which the service is registered, i.e. the domain passed
219 to the serviceFound() callback.
220 <P>
221 @param listener
222 This object will get called when the service is resolved.
223 <P>
224 @return A {@link DNSSDService} that represents the active resolve operation.
225
226 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
227 @see RuntimePermission
228 */
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); }
233
234 /** Register a service, to be discovered via browse() and resolve() calls.<P>
235 @param flags
236 Possible values are: NO_AUTO_RENAME.
237 <P>
238 @param ifIndex
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).
244 <P>
245 @param serviceName
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).
250 <P>
251 @param regType
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".
254 <P>
255 @param domain
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).
259 <P>
260 @param host
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}.
267 <P>
268 @param port
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.
273 <P>
274 @param txtRecord
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. &lt;length byte&gt; &lt;data&gt;
277 &lt;length byte&gt; &lt;data&gt; ...
278 <P>
279 @param listener
280 This object will get called when the service is registered.
281 <P>
282 @return A {@link DNSSDRegistration} that controls the active registration.
283
284 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
285 @see RuntimePermission
286 */
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); }
291
292 /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.<P>
293 @param serviceName
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).
298 <P>
299 @param regType
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".
302 <P>
303 @param port
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.
308 <P>
309 @param listener
310 This object will get called when the service is registered.
311 <P>
312 @return A {@link DNSSDRegistration} that controls the active registration.
313
314 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
315 @see RuntimePermission
316 */
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); }
320
321 /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of
322 multiple individual records.<P>
323 <P>
324 @return A {@link DNSSDRecordRegistrar} that can be used to register records.
325
326 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
327 @see RuntimePermission
328 */
329 public static DNSSDRecordRegistrar createRecordRegistrar( RegisterRecordListener listener)
330 throws DNSSDException
331 { return getInstance()._createRecordRegistrar( listener); }
332
333 /** Query for an arbitrary DNS record.<P>
334 @param flags
335 Possible values are: MORE_COMING.
336 <P>
337 @param ifIndex
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
342 local host.
343 <P>
344 @param serviceName
345 The full domain name of the resource record to be queried for.
346 <P>
347 @param rrtype
348 The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
349 as defined in nameser.h.
350 <P>
351 @param rrclass
352 The class of the resource record, as defined in nameser.h
353 (usually 1 for the Internet class).
354 <P>
355 @param listener
356 This object will get called when the query completes.
357 <P>
358 @return A {@link DNSSDService} that controls the active query.
359
360 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
361 @see RuntimePermission
362 */
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); }
367
368 /** Asynchronously enumerate domains available for browsing and registration.<P>
369
370 Currently, the only domain returned is "local.", but other domains will be returned in future.<P>
371
372 The enumeration MUST be cancelled by calling {@link DNSSDService#stop} when no more domains
373 are to be found.<P>
374 @param flags
375 Possible values are: BROWSE_DOMAINS, REGISTRATION_DOMAINS.
376 <P>
377 @param ifIndex
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
381 all interfaces.
382 <P>
383 @param listener
384 This object will get called when domains are found.
385 <P>
386 @return A {@link DNSSDService} that controls the active enumeration.
387
388 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
389 @see RuntimePermission
390 */
391 public static DNSSDService enumerateDomains( int flags, int ifIndex, DomainListener listener)
392 throws DNSSDException
393 { return getInstance()._enumerateDomains( flags, ifIndex, listener); }
394
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>
398 @param serviceName
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").
401 <P>
402 @param regType
403 The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").
404 <P>
405 @param domain
406 The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped.
407 <P>
408 @return The full domain name.
409
410 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
411 @see RuntimePermission
412 */
413 public static String constructFullName( String serviceName, String regType, String domain)
414 throws DNSSDException
415 { return getInstance()._constructFullName( serviceName, regType, domain); }
416
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>
419
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>
422 @param flags
423 Currently unused, reserved for future use.
424 <P>
425 @param ifIndex
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
430 local host.
431 <P>
432 @param fullName
433 The resource record's full domain name.
434 <P>
435 @param rrtype
436 The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
437 <P>
438 @param rrclass
439 The class of the resource record, as defined in nameser.h (usually 1).
440 <P>
441 @param rdata
442 The raw rdata of the resource record.
443
444 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
445 @see RuntimePermission
446 */
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); }
450
451 /** Return the canonical name of a particular interface index.<P>
452 @param ifIndex
453 A valid interface index. Must not be ALL_INTERFACES.
454 <P>
455 @return The name of the interface, which should match java.net.NetworkInterface.getName().
456
457 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
458 @see RuntimePermission
459 */
460 public static String getNameForIfIndex( int ifIndex)
461 { return getInstance()._getNameForIfIndex( ifIndex); }
462
463 /** Return the index of a named interface.<P>
464 @param ifName
465 A valid interface name. An example is java.net.NetworkInterface.getName().
466 <P>
467 @return The interface index.
468
469 @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
470 @see RuntimePermission
471 */
472 public static int getIfIndexForName( String ifName)
473 { return getInstance()._getIfIndexForName( ifName); }
474
475 protected DNSSD() {} // prevent direct instantiation
476
477 /** Return the single instance of DNSSD. */
478 static protected final DNSSD getInstance()
479 {
480 SecurityManager sm = System.getSecurityManager();
481 if ( sm != null)
482 sm.checkPermission( new RuntimePermission( "getDNSSDInstance"));
483 return fInstance;
484 }
485
486 abstract protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener listener)
487 throws DNSSDException;
488
489 abstract protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType,
490 String domain, ResolveListener listener)
491 throws DNSSDException;
492
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;
496
497 abstract protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener)
498 throws DNSSDException;
499
500 abstract protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
501 int rrclass, QueryListener listener)
502 throws DNSSDException;
503
504 abstract protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener)
505 throws DNSSDException;
506
507 abstract protected String _constructFullName( String serviceName, String regType, String domain)
508 throws DNSSDException;
509
510 abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
511 int rrclass, byte[] rdata);
512
513 abstract protected String _getNameForIfIndex( int ifIndex);
514
515 abstract protected int _getIfIndexForName( String ifName);
516
517 protected static DNSSD fInstance;
518
519 static
520 {
521 try
522 {
523 String name = System.getProperty( "com.apple.dnssd.DNSSD" );
524 if( name == null )
525 name = "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class.
526 fInstance = (DNSSD) Class.forName( name ).newInstance();
527 }
528 catch( Exception e )
529 {
530 throw new InternalError( "cannot instantiate DNSSD" + e );
531 }
532 }
533 }
534
535
536 // Concrete implementation of DNSSDException
537 class AppleDNSSDException extends DNSSDException
538 {
539 public AppleDNSSDException( int errorCode) { fErrorCode = errorCode; }
540
541 public int getErrorCode() { return fErrorCode; }
542
543 public String getMessage()
544 {
545 final String kMessages[] = { // should probably be put into a resource or something
546 "UNKNOWN",
547 "NO_SUCH_NAME",
548 "NO_MEMORY",
549 "BAD_PARAM",
550 "BAD_REFERENCE",
551 "BAD_STATE",
552 "BAD_FLAGS",
553 "UNSUPPORTED",
554 "NOT_INITIALIZED",
555 "NO_CACHE",
556 "ALREADY_REGISTERED",
557 "NAME_CONFLICT",
558 "INVALID",
559 "FIREWALL",
560 "INCOMPATIBLE",
561 "BAD_INTERFACE_INDEX",
562 "REFUSED",
563 "NOSUCHRECORD",
564 "NOAUTH",
565 "NOSUCHKEY",
566 "NATTRAVERSAL",
567 "DOUBLENAT",
568 "BADTIME",
569 "BADSIG",
570 "BADKEY",
571 "TRANSIENT",
572 "SERVICENOTRUNNING",
573 "NATPORTMAPPINGUNSUPPORTED",
574 "NATPORTMAPPINGDISABLED"
575 };
576
577 if ( fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length))
578 {
579 return "DNS-SD Error " + String.valueOf( fErrorCode) + ": " + kMessages[ UNKNOWN - fErrorCode];
580 }
581 else
582 return super.getMessage() + "(" + String.valueOf( fErrorCode) + ")";
583 }
584
585 protected int fErrorCode;
586 }
587
588 // The concrete, default implementation.
589 class AppleDNSSD extends DNSSD
590 {
591 static
592 {
593 System.loadLibrary( "jdns_sd");
594
595 int libInitResult = InitLibrary( 2); // Current version number (must be sync'd with jnilib version)
596
597 if ( libInitResult != DNSSDException.NO_ERROR)
598 throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult).getMessage());
599 }
600
601 static public boolean hasAutoCallbacks; // Set by InitLibrary() to value of AUTO_CALLBACKS
602
603 protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)
604 throws DNSSDException
605 {
606 return new AppleBrowser( flags, ifIndex, regType, domain, client);
607 }
608
609 protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType,
610 String domain, ResolveListener client)
611 throws DNSSDException
612 {
613 return new AppleResolver( flags, ifIndex, serviceName, regType, domain, client);
614 }
615
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
619 {
620 return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port,
621 ( txtRecord != null) ? txtRecord.getRawBytes() : null, client);
622 }
623
624 protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener)
625 throws DNSSDException
626 {
627 return new AppleRecordRegistrar( listener);
628 }
629
630 protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
631 int rrclass, QueryListener client)
632 throws DNSSDException
633 {
634 return new AppleQuery( flags, ifIndex, serviceName, rrtype, rrclass, client);
635 }
636
637 protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener)
638 throws DNSSDException
639 {
640 return new AppleDomainEnum( flags, ifIndex, listener);
641 }
642
643 protected String _constructFullName( String serviceName, String regType, String domain)
644 throws DNSSDException
645 {
646 String[] responseHolder = new String[1]; // lame maneuver to get around Java's lack of reference parameters
647
648 int rc = ConstructName( serviceName, regType, domain, responseHolder);
649 if ( rc != 0)
650 throw new AppleDNSSDException( rc);
651
652 return responseHolder[0];
653 }
654
655 protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
656 int rrclass, byte[] rdata)
657 {
658 ReconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata);
659 }
660
661 protected String _getNameForIfIndex( int ifIndex)
662 {
663 return GetNameForIfIndex( ifIndex);
664 }
665
666 protected int _getIfIndexForName( String ifName)
667 {
668 return GetIfIndexForName( ifName);
669 }
670
671
672 protected native int ConstructName( String serviceName, String regType, String domain, String[] pOut);
673
674 protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
675 int rrclass, byte[] rdata);
676
677 protected native String GetNameForIfIndex( int ifIndex);
678
679 protected native int GetIfIndexForName( String ifName);
680
681 protected static native int InitLibrary( int callerVersion);
682 }
683
684 class AppleService implements DNSSDService, Runnable
685 {
686 public AppleService(BaseListener listener) { fNativeContext = 0; fListener = listener; }
687
688 public void stop() { this.HaltOperation(); }
689
690 /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
691 protected native int BlockForData();
692
693 /* Call ProcessResults when data appears on socket descriptor. */
694 protected native int ProcessResults();
695
696 protected synchronized native void HaltOperation();
697
698 protected void ThrowOnErr( int rc) throws DNSSDException
699 {
700 if ( rc != 0)
701 throw new AppleDNSSDException( rc);
702 }
703
704 protected long /* warning */ fNativeContext; // Private storage for native side
705
706 public void run()
707 {
708 while ( true )
709 {
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.
722 //
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.
730
731 int result = BlockForData();
732 synchronized (this)
733 {
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
739 }
740 }
741 }
742
743 protected BaseListener fListener;
744 }
745
746
747 class AppleBrowser extends AppleService
748 {
749 public AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)
750 throws DNSSDException
751 {
752 super(client);
753 this.ThrowOnErr( this.CreateBrowser( flags, ifIndex, regType, domain));
754 if ( !AppleDNSSD.hasAutoCallbacks)
755 new Thread(this).start();
756 }
757
758 // Sets fNativeContext. Returns non-zero on error.
759 protected native int CreateBrowser( int flags, int ifIndex, String regType, String domain);
760 }
761
762 class AppleResolver extends AppleService
763 {
764 public AppleResolver( int flags, int ifIndex, String serviceName, String regType,
765 String domain, ResolveListener client)
766 throws DNSSDException
767 {
768 super(client);
769 this.ThrowOnErr( this.CreateResolver( flags, ifIndex, serviceName, regType, domain));
770 if ( !AppleDNSSD.hasAutoCallbacks)
771 new Thread(this).start();
772 }
773
774 // Sets fNativeContext. Returns non-zero on error.
775 protected native int CreateResolver( int flags, int ifIndex, String serviceName, String regType,
776 String domain);
777 }
778
779 // An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord.
780 class AppleDNSRecord implements DNSRecord
781 {
782 public AppleDNSRecord( AppleService owner)
783 {
784 fOwner = owner;
785 fRecord = 0; // record always starts out empty
786 }
787
788 public void update( int flags, byte[] rData, int ttl)
789 throws DNSSDException
790 {
791 this.ThrowOnErr( this.Update( flags, rData, ttl));
792 }
793
794 public void remove()
795 throws DNSSDException
796 {
797 this.ThrowOnErr( this.Remove());
798 }
799
800 protected long fRecord; // Really a DNSRecord; sizeof(long) == sizeof(void*) ?
801 protected AppleService fOwner;
802
803 protected void ThrowOnErr( int rc) throws DNSSDException
804 {
805 if ( rc != 0)
806 throw new AppleDNSSDException( rc);
807 }
808
809 protected native int Update( int flags, byte[] rData, int ttl);
810
811 protected native int Remove();
812 }
813
814 class AppleRegistration extends AppleService implements DNSSDRegistration
815 {
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
819 {
820 super(client);
821 this.ThrowOnErr( this.BeginRegister( ifIndex, flags, serviceName, regType, domain, host, port, txtRecord));
822 if ( !AppleDNSSD.hasAutoCallbacks)
823 new Thread(this).start();
824 }
825
826 public DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl)
827 throws DNSSDException
828 {
829 AppleDNSRecord newRecord = new AppleDNSRecord( this);
830
831 this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord));
832 return newRecord;
833 }
834
835 public DNSRecord getTXTRecord()
836 throws DNSSDException
837 {
838 return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record
839 }
840
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);
844
845 // Sets fNativeContext. Returns non-zero on error.
846 protected native int AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj);
847 }
848
849 class AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar
850 {
851 public AppleRecordRegistrar( RegisterRecordListener listener)
852 throws DNSSDException
853 {
854 super(listener);
855 this.ThrowOnErr( this.CreateConnection());
856 if ( !AppleDNSSD.hasAutoCallbacks)
857 new Thread(this).start();
858 }
859
860 public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype,
861 int rrclass, byte[] rdata, int ttl)
862 throws DNSSDException
863 {
864 AppleDNSRecord newRecord = new AppleDNSRecord( this);
865
866 this.ThrowOnErr( this.RegisterRecord( flags, ifIndex, fullname, rrtype, rrclass, rdata, ttl, newRecord));
867 return newRecord;
868 }
869
870 // Sets fNativeContext. Returns non-zero on error.
871 protected native int CreateConnection();
872
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);
876 }
877
878 class AppleQuery extends AppleService
879 {
880 public AppleQuery( int flags, int ifIndex, String serviceName, int rrtype,
881 int rrclass, QueryListener client)
882 throws DNSSDException
883 {
884 super(client);
885 this.ThrowOnErr( this.CreateQuery( flags, ifIndex, serviceName, rrtype, rrclass));
886 if ( !AppleDNSSD.hasAutoCallbacks)
887 new Thread(this).start();
888 }
889
890 // Sets fNativeContext. Returns non-zero on error.
891 protected native int CreateQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass);
892 }
893
894 class AppleDomainEnum extends AppleService
895 {
896 public AppleDomainEnum( int flags, int ifIndex, DomainListener client)
897 throws DNSSDException
898 {
899 super(client);
900 this.ThrowOnErr( this.BeginEnum( flags, ifIndex));
901 if ( !AppleDNSSD.hasAutoCallbacks)
902 new Thread(this).start();
903 }
904
905 // Sets fNativeContext. Returns non-zero on error.
906 protected native int BeginEnum( int flags, int ifIndex);
907 }
908
909