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