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