X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/blobdiff_plain/7f0064bd55e3fa98568d2c359429ff8a38b23a6c..7dc706029db60e343022e1be32a35a5958ad1a84:/mDNSShared/Java/DNSSD.java diff --git a/mDNSShared/Java/DNSSD.java b/mDNSShared/Java/DNSSD.java index 831b4b4..e608869 100644 --- a/mDNSShared/Java/DNSSD.java +++ b/mDNSShared/Java/DNSSD.java @@ -1,28 +1,58 @@ -/* +/* -*- Mode: Java; tab-width: 4 -*- + * * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: DNSSD.java,v $ +Revision 1.16 2008/11/04 20:06:20 cheshire + Change MAX_DOMAIN_NAME to 256 + +Revision 1.15 2007/03/13 00:28:03 vazquez + Java: Rename exported symbols in libjdns_sd.jnilib + +Revision 1.14 2007/03/13 00:10:14 vazquez + Java: 64 bit JNI patch + +Revision 1.13 2007/02/24 23:08:02 mkrochma + Typo in Bonjour Java API document + +Revision 1.12 2007/02/09 00:33:02 cheshire +Add missing error codes to kMessages array + +Revision 1.11 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.10 2006/06/20 23:05:55 rpantos + Java needs to implement DNSServiceRegisterRecord equivalent + +Revision 1.9 2005/10/26 01:52:24 cheshire + Race condition in Java code (doesn't work at all on Linux) + +Revision 1.8 2005/07/11 01:55:21 cheshire + Race condition in Java API + +Revision 1.7 2005/07/05 13:01:52 cheshire + If mDNSResponder daemon is stopped, Java API spins, burning CPU time + +Revision 1.6 2005/07/05 00:02:25 cheshire +Add missing comma + +Revision 1.5 2005/07/04 21:13:47 cheshire +Add missing error message strings + Revision 1.4 2004/12/11 03:00:59 rpantos Java DNSRecord API should be cleaned up @@ -39,10 +69,6 @@ First checked in. This file declares and implements DNSSD, the central Java factory class for doing DNS Service Discovery. It includes the mostly-abstract public interface, as well as the Apple* implementation subclasses. - - To do: - - implement network interface mappings - - RegisterRecord */ @@ -53,19 +79,19 @@ package com.apple.dnssd; DNSSD provides access to DNS Service Discovery features of ZeroConf networking.

It is a factory class that is used to invoke registration and discovery-related - operations. Most operations are non-blocking; clients are called back through an interface + operations. Most operations are non-blocking; clients are called back through an interface with the result of an operation. Callbacks are made from a separate worker thread.

- For example, in this program

+ For example, in this program


     class   MyClient implements BrowseListener {
         void    lookForWebServers() {
-            myBrowser = DNSSD.browse("_http_.tcp", this);
+            myBrowser = DNSSD.browse("_http._tcp", this);
         }
     
-        public void serviceFound(DNSSDService browser, int flags, int ifIndex, 
+        public void serviceFound(DNSSDService browser, int flags, int ifIndex,
                             String serviceName, String regType, String domain) {}
-        ... 
+        ...
     }
MyClient.serviceFound() would be called for every HTTP server discovered in the default browse domain(s). @@ -77,14 +103,14 @@ abstract public class DNSSD queued. Applications should not update their UI to display browse results if the MORE_COMING flag is set; they will be called at least once more with the flag clear. - */ + */ public static final int MORE_COMING = ( 1 << 0 ); /** If flag is set in a {@link DomainListener} callback, indicates that the result is the default domain. */ public static final int DEFAULT = ( 1 << 2 ); - /** If flag is set, a name conflict will trigger an exception when registering non-shared records.

- A name must be explicitly specified when registering a service if this bit is set + /** If flag is set, a name conflict will trigger an exception when registering non-shared records.

+ A name must be explicitly specified when registering a service if this bit is set (i.e. the default name may not not be used). */ public static final int NO_AUTO_RENAME = ( 1 << 3 ); @@ -103,7 +129,7 @@ abstract public class DNSSD public static final int REGISTRATION_DOMAINS = ( 1 << 7 ); /** Maximum length, in bytes, of a domain name represented as an escaped C-String. */ - public static final int MAX_DOMAIN_NAME = 1005; + public static final int MAX_DOMAIN_NAME = 1009; /** Pass for ifIndex to specify all available interfaces. */ public static final int ALL_INTERFACES = 0; @@ -111,7 +137,7 @@ abstract public class DNSSD /** Pass for ifIndex to specify the localhost interface. */ public static final int LOCALHOST_ONLY = -1; - /** Browse for instances of a service.

+ /** Browse for instances of a service.

Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.

@@ -125,12 +151,12 @@ abstract public class DNSSD interfaces. Pass -1 to only browse for services provided on the local host.

@param regType - The registration type being browsed for followed by the protocol, separated by a + The registration type being browsed for followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".

@param domain If non-null, specifies the domain on which to browse for services. - Most applications will not specify a domain, instead browsing on the + Most applications will not specify a domain, instead browsing on the default domain(s).

@param listener @@ -145,10 +171,10 @@ abstract public class DNSSD throws DNSSDException { return getInstance()._makeBrowser( flags, ifIndex, regType, domain, listener); } - /** Browse for instances of a service. Use default flags, ifIndex and domain.

+ /** Browse for instances of a service. Use default flags, ifIndex and domain.

@param regType - The registration type being browsed for followed by the protocol, separated by a + The registration type being browsed for followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".

@param listener @@ -163,16 +189,16 @@ abstract public class DNSSD throws DNSSDException { return browse( 0, 0, regType, "", listener); } - /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.

+ /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.

- Note: Applications should NOT use resolve() solely for txt record monitoring - use + Note: Applications should NOT use resolve() solely for txt record monitoring - use queryRecord() instead, as it is more efficient for this task.

- Note: When the desired results have been returned, the client MUST terminate the resolve by + Note: When the desired results have been returned, the client MUST terminate the resolve by calling {@link DNSSDService#stop}.

Note: resolve() behaves correctly for typical services that have a single SRV record and - a single TXT record (the TXT record may be empty.) To resolve non-standard services with + a single TXT record (the TXT record may be empty.) To resolve non-standard services with multiple SRV or TXT records, use queryRecord().

@param flags @@ -180,7 +206,7 @@ abstract public class DNSSD

@param ifIndex The interface on which to resolve the service. The client should - pass the interface on which the serviceName was discovered (i.e. + pass the interface on which the serviceName was discovered (i.e. the ifIndex passed to the serviceFound() callback) or 0 to resolve the named service on all available interfaces.

@@ -188,8 +214,8 @@ abstract public class DNSSD The servicename to be resolved.

@param regType - The registration type being resolved followed by the protocol, separated by a - dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + The registration type being resolved followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".

@param domain The domain on which the service is registered, i.e. the domain passed @@ -203,54 +229,54 @@ abstract public class DNSSD @throws SecurityException If a security manager is present and denies RuntimePermission("getDNSSDInstance"). @see RuntimePermission */ - public static DNSSDService resolve( int flags, int ifIndex, String serviceName, String regType, + public static DNSSDService resolve( int flags, int ifIndex, String serviceName, String regType, String domain, ResolveListener listener) throws DNSSDException { return getInstance()._resolve( flags, ifIndex, serviceName, regType, domain, listener); } - /** Register a service, to be discovered via browse() and resolve() calls.

+ /** Register a service, to be discovered via browse() and resolve() calls.

@param flags Possible values are: NO_AUTO_RENAME.

@param ifIndex If non-zero, specifies the interface on which to register the service (the index for a given interface is determined via the if_nametoindex() - family of calls.) Most applications will pass 0 to register on all - available interfaces. Pass -1 to register a service only on the local + family of calls.) Most applications will pass 0 to register on all + available interfaces. Pass -1 to register a service only on the local machine (service will not be visible to remote hosts).

@param serviceName - If non-null, specifies the service name to be registered. - Applications need not specify a name, in which case the - computer name is used (this name is communicated to the client via + If non-null, specifies the service name to be registered. + Applications need not specify a name, in which case the + computer name is used (this name is communicated to the client via the serviceRegistered() callback).

@param regType - The registration type being registered followed by the protocol, separated by a - dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + The registration type being registered followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".

@param domain If non-null, specifies the domain on which to advertise the service. - Most applications will not specify a domain, instead automatically + Most applications will not specify a domain, instead automatically registering in the default domain(s).

@param host If non-null, specifies the SRV target host name. Most applications will not specify a host, instead automatically using the machine's - default host name(s). Note that specifying a non-null host does NOT - create an address record for that host - the application is responsible + default host name(s). Note that specifying a non-null host does NOT + create an address record for that host - the application is responsible for ensuring that the appropriate address record exists, or creating it via {@link DNSSDRegistration#addRecord}.

@param port - The port on which the service accepts connections. Pass 0 for a - "placeholder" service (i.e. a service that will not be discovered by - browsing, but will cause a name conflict if another client tries to + The port on which the service accepts connections. Pass 0 for a + "placeholder" service (i.e. a service that will not be discovered by + browsing, but will cause a name conflict if another client tries to register that same name.) Most clients will not use placeholder services.

@param txtRecord - The txt record rdata. May be null. Note that a non-null txtRecord - MUST be a properly formatted DNS TXT record, i.e. <length byte> <data> + The txt record rdata. May be null. Note that a non-null txtRecord + MUST be a properly formatted DNS TXT record, i.e. <length byte> <data> <length byte> <data> ...

@param listener @@ -261,26 +287,26 @@ abstract public class DNSSD @throws SecurityException If a security manager is present and denies RuntimePermission("getDNSSDInstance"). @see RuntimePermission */ - public static DNSSDRegistration register( int flags, int ifIndex, String serviceName, String regType, + public static DNSSDRegistration register( int flags, int ifIndex, String serviceName, String regType, String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener) throws DNSSDException { return getInstance()._register( flags, ifIndex, serviceName, regType, domain, host, port, txtRecord, listener); } - /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.

+ /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.

@param serviceName - If non-null, specifies the service name to be registered. - Applications need not specify a name, in which case the - computer name is used (this name is communicated to the client via + If non-null, specifies the service name to be registered. + Applications need not specify a name, in which case the + computer name is used (this name is communicated to the client via the serviceRegistered() callback).

@param regType - The registration type being registered followed by the protocol, separated by a - dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + The registration type being registered followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".

@param port - The port on which the service accepts connections. Pass 0 for a - "placeholder" service (i.e. a service that will not be discovered by - browsing, but will cause a name conflict if another client tries to + The port on which the service accepts connections. Pass 0 for a + "placeholder" service (i.e. a service that will not be discovered by + browsing, but will cause a name conflict if another client tries to register that same name.) Most clients will not use placeholder services.

@param listener @@ -295,14 +321,26 @@ abstract public class DNSSD throws DNSSDException { return register( 0, 0, serviceName, regType, null, null, port, null, listener); } - /** Query for an arbitrary DNS record.

+ /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of + multiple individual records.

+

+ @return A {@link DNSSDRecordRegistrar} that can be used to register records. + + @throws SecurityException If a security manager is present and denies RuntimePermission("getDNSSDInstance"). + @see RuntimePermission + */ + public static DNSSDRecordRegistrar createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { return getInstance()._createRecordRegistrar( listener); } + + /** Query for an arbitrary DNS record.

@param flags Possible values are: MORE_COMING.

@param ifIndex If non-zero, specifies the interface on which to issue the query (the index for a given interface is determined via the if_nametoindex() - family of calls.) Passing 0 causes the name to be queried for on all + family of calls.) Passing 0 causes the name to be queried for on all interfaces. Passing -1 causes the name to be queried for only on the local host.

@@ -314,7 +352,7 @@ abstract public class DNSSD as defined in nameser.h.

@param rrclass - The class of the resource record, as defined in nameser.h + The class of the resource record, as defined in nameser.h (usually 1 for the Internet class).

@param listener @@ -325,12 +363,12 @@ abstract public class DNSSD @throws SecurityException If a security manager is present and denies RuntimePermission("getDNSSDInstance"). @see RuntimePermission */ - public static DNSSDService queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + public static DNSSDService queryRecord( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener listener) throws DNSSDException { return getInstance()._queryRecord( flags, ifIndex, serviceName, rrtype, rrclass, listener); } - /** Asynchronously enumerate domains available for browsing and registration.

+ /** Asynchronously enumerate domains available for browsing and registration.

Currently, the only domain returned is "local.", but other domains will be returned in future.

@@ -342,8 +380,8 @@ abstract public class DNSSD @param ifIndex If non-zero, specifies the interface on which to look for domains. (the index for a given interface is determined via the if_nametoindex() - family of calls.) Most applications will pass 0 to enumerate domains on - all interfaces. + family of calls.) Most applications will pass 0 to enumerate domains on + all interfaces.

@param listener This object will get called when domains are found. @@ -357,15 +395,15 @@ abstract public class DNSSD throws DNSSDException { return getInstance()._enumerateDomains( flags, ifIndex, listener); } - /** Concatenate a three-part domain name (as provided to the listeners) into a - properly-escaped full domain name. Note that strings passed to listeners are - ALREADY ESCAPED where necessary.

+ /** Concatenate a three-part domain name (as provided to the listeners) into a + properly-escaped full domain name. Note that strings passed to listeners are + ALREADY ESCAPED where necessary.

@param serviceName The service name - any dots or slashes must NOT be escaped. May be null (to construct a PTR record name, e.g. "_ftp._tcp.apple.com").

@param regType - The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). + The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").

@param domain The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped. @@ -379,10 +417,10 @@ abstract public class DNSSD throws DNSSDException { return getInstance()._constructFullName( serviceName, regType, domain); } - /** Instruct the daemon to verify the validity of a resource record that appears to - be out of date. (e.g. because tcp connection to a service's target failed.)

+ /** Instruct the daemon to verify the validity of a resource record that appears to + be out of date. (e.g. because tcp connection to a service's target failed.)

- Causes the record to be flushed from the daemon's cache (as well as all other + Causes the record to be flushed from the daemon's cache (as well as all other daemons' caches on the network) if the record is determined to be invalid.

@param flags Currently unused, reserved for future use. @@ -390,7 +428,7 @@ abstract public class DNSSD @param ifIndex If non-zero, specifies the interface on which to reconfirm the record (the index for a given interface is determined via the if_nametoindex() - family of calls.) Passing 0 causes the name to be reconfirmed on all + family of calls.) Passing 0 causes the name to be reconfirmed on all interfaces. Passing -1 causes the name to be reconfirmed only on the local host.

@@ -409,11 +447,11 @@ abstract public class DNSSD @throws SecurityException If a security manager is present and denies RuntimePermission("getDNSSDInstance"). @see RuntimePermission */ - public static void reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + public static void reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata) { getInstance()._reconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); } - /** Return the canonical name of a particular interface index.

+ /** Return the canonical name of a particular interface index.

@param ifIndex A valid interface index. Must not be ALL_INTERFACES.

@@ -425,7 +463,7 @@ abstract public class DNSSD public static String getNameForIfIndex( int ifIndex) { return getInstance()._getNameForIfIndex( ifIndex); } - /** Return the index of a named interface.

+ /** Return the index of a named interface.

@param ifName A valid interface name. An example is java.net.NetworkInterface.getName().

@@ -440,26 +478,29 @@ abstract public class DNSSD protected DNSSD() {} // prevent direct instantiation /** Return the single instance of DNSSD. */ - static protected final DNSSD getInstance() + static protected final DNSSD getInstance() { SecurityManager sm = System.getSecurityManager(); - if ( sm != null) - sm.checkPermission( new RuntimePermission( "getDNSSDInstance")); - return fInstance; + if ( sm != null) + sm.checkPermission( new RuntimePermission( "getDNSSDInstance")); + return fInstance; } abstract protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener listener) throws DNSSDException; - abstract protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, + abstract protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, String domain, ResolveListener listener) throws DNSSDException; - abstract protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType, + abstract protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType, String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener) throws DNSSDException; - abstract protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + abstract protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException; + + abstract protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener listener) throws DNSSDException; @@ -469,7 +510,7 @@ abstract public class DNSSD abstract protected String _constructFullName( String serviceName, String regType, String domain) throws DNSSDException; - abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata); abstract protected String _getNameForIfIndex( int ifIndex); @@ -481,7 +522,7 @@ abstract public class DNSSD static { try - { + { String name = System.getProperty( "com.apple.dnssd.DNSSD" ); if( name == null ) name = "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class. @@ -514,13 +555,26 @@ class AppleDNSSDException extends DNSSDException "BAD_FLAGS", "UNSUPPORTED", "NOT_INITIALIZED", - "", // there is NO number 6 + "NO_CACHE", "ALREADY_REGISTERED", "NAME_CONFLICT", "INVALID", - "", // another MIA + "FIREWALL", "INCOMPATIBLE", - "BAD_INTERFACE_INDEX" + "BAD_INTERFACE_INDEX", + "REFUSED", + "NOSUCHRECORD", + "NOAUTH", + "NOSUCHKEY", + "NATTRAVERSAL", + "DOUBLENAT", + "BADTIME", + "BADSIG", + "BADKEY", + "TRANSIENT", + "SERVICENOTRUNNING", + "NATPORTMAPPINGUNSUPPORTED", + "NATPORTMAPPINGDISABLED" }; if ( fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length)) @@ -541,7 +595,7 @@ class AppleDNSSD extends DNSSD { System.loadLibrary( "jdns_sd"); - int libInitResult = InitLibrary( 1); + int libInitResult = InitLibrary( 2); // Current version number (must be sync'd with jnilib version) if ( libInitResult != DNSSDException.NO_ERROR) throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult).getMessage()); @@ -555,22 +609,28 @@ class AppleDNSSD extends DNSSD return new AppleBrowser( flags, ifIndex, regType, domain, client); } - protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, + protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, String domain, ResolveListener client) throws DNSSDException { return new AppleResolver( flags, ifIndex, serviceName, regType, domain, client); } - protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType, + protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType, String domain, String host, int port, TXTRecord txtRecord, RegisterListener client) throws DNSSDException { - return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port, + return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port, ( txtRecord != null) ? txtRecord.getRawBytes() : null, client); } - protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { + return new AppleRecordRegistrar( listener); + } + + protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, int rrclass, QueryListener client) throws DNSSDException { @@ -595,7 +655,7 @@ class AppleDNSSD extends DNSSD return responseHolder[0]; } - protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata) { ReconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); @@ -614,7 +674,7 @@ class AppleDNSSD extends DNSSD protected native int ConstructName( String serviceName, String regType, String domain, String[] pOut); - protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata); protected native String GetNameForIfIndex( int ifIndex); @@ -626,29 +686,17 @@ class AppleDNSSD extends DNSSD class AppleService implements DNSSDService, Runnable { - public AppleService() { fNativeContext = 0; } + public AppleService(BaseListener listener) { fNativeContext = 0; fListener = listener; } public void stop() { this.HaltOperation(); } - public void finalize() throws Throwable - { - this.stop(); - super.finalize(); - } - - /* The run() method is used internally to schedule an update from another thread */ - public void run() - { - this.ProcessResults(); - } - - /* Block for timeout ms (or forever if -1). Returns 1 if data present, 0 if timed out, -1 if not browsing. */ - protected native int BlockForData( int msTimeout); + /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */ + protected native int BlockForData(); /* Call ProcessResults when data appears on socket descriptor. */ - protected native void ProcessResults(); + protected native int ProcessResults(); - protected native void HaltOperation(); + protected synchronized native void HaltOperation(); protected void ThrowOnErr( int rc) throws DNSSDException { @@ -656,78 +704,87 @@ class AppleService implements DNSSDService, Runnable throw new AppleDNSSDException( rc); } - protected int /* warning */ fNativeContext; // Private storage for native side -} - - -// A ServiceThread calls AppleService.BlockForData() and schedules its client -// when data appears. -class ServiceThread extends Thread -{ - public ServiceThread( AppleService owner) { fOwner = owner; } + protected long /* warning */ fNativeContext; // Private storage for native side public void run() { - int result; - while ( true ) { - result = fOwner.BlockForData( -1); - if ( result == 1) + // Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to + // block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized + // we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread, + // and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD + // operation. The Unix kernel always allocates the lowest available file descriptor to a new socket, + // so the same file descriptor is highly likely to be reused for the new operation, and if our old + // stale ServiceThread accidentally consumes bytes off that new socket we'll get really messed up. + // To guard against that, before calling ProcessResults we check to ensure that our + // fNativeContext has not been deleted, which is a telltale sign that our operation was stopped. + // After calling ProcessResults we check again, because it's extremely common for callback + // functions to stop their own operation and start others. For example, a resolveListener callback + // may well stop the resolve and then start a QueryRecord call to monitor the TXT record. + // + // The remaining risk is that between our checking fNativeContext and calling ProcessResults(), + // some other thread could stop the operation and start a new one using same file descriptor, and + // we wouldn't know. To prevent this, the AppleService object's HaltOperation() routine is declared + // synchronized and we perform our checks synchronized on the AppleService object, which ensures + // that HaltOperation() can't execute while we're doing it. Because Java locks are re-entrant this + // locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent + // any other thread from stopping it until after the callback has completed and returned to us here. + + int result = BlockForData(); + synchronized (this) { - fOwner.run(); + if (fNativeContext == 0) break; // Some other thread stopped our DNSSD operation; time to terminate this thread + if (result == 0) continue; // If BlockForData() said there was no data, go back and block again + result = ProcessResults(); + if (fNativeContext == 0) break; // Event listener stopped its own DNSSD operation; terminate this thread + if (result != 0) { fListener.operationFailed(this, result); break; } // If error, notify listener } - else - break; // terminate thread } } - protected AppleService fOwner; + protected BaseListener fListener; } class AppleBrowser extends AppleService { - public AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client) + public AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client) throws DNSSDException - { - fClient = client; + { + super(client); this.ThrowOnErr( this.CreateBrowser( flags, ifIndex, regType, domain)); if ( !AppleDNSSD.hasAutoCallbacks) - new ServiceThread( this).start(); + new Thread(this).start(); } // Sets fNativeContext. Returns non-zero on error. protected native int CreateBrowser( int flags, int ifIndex, String regType, String domain); - - protected BrowseListener fClient; } class AppleResolver extends AppleService { - public AppleResolver( int flags, int ifIndex, String serviceName, String regType, - String domain, ResolveListener client) + public AppleResolver( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener client) throws DNSSDException - { - fClient = client; + { + super(client); this.ThrowOnErr( this.CreateResolver( flags, ifIndex, serviceName, regType, domain)); if ( !AppleDNSSD.hasAutoCallbacks) - new ServiceThread( this).start(); + new Thread(this).start(); } // Sets fNativeContext. Returns non-zero on error. - protected native int CreateResolver( int flags, int ifIndex, String serviceName, String regType, + protected native int CreateResolver( int flags, int ifIndex, String serviceName, String regType, String domain); - - protected ResolveListener fClient; } // An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord. class AppleDNSRecord implements DNSRecord { - public AppleDNSRecord( AppleService owner) - { - fOwner = owner; + public AppleDNSRecord( AppleService owner) + { + fOwner = owner; fRecord = 0; // record always starts out empty } @@ -743,7 +800,7 @@ class AppleDNSRecord implements DNSRecord this.ThrowOnErr( this.Remove()); } - protected int fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ? + protected long fRecord; // Really a DNSRecord; sizeof(long) == sizeof(void*) ? protected AppleService fOwner; protected void ThrowOnErr( int rc) throws DNSSDException @@ -759,14 +816,14 @@ class AppleDNSRecord implements DNSRecord class AppleRegistration extends AppleService implements DNSSDRegistration { - public AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain, - String host, int port, byte[] txtRecord, RegisterListener client) + public AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain, + String host, int port, byte[] txtRecord, RegisterListener client) throws DNSSDException - { - fClient = client; + { + super(client); this.ThrowOnErr( this.BeginRegister( ifIndex, flags, serviceName, regType, domain, host, port, txtRecord)); if ( !AppleDNSSD.hasAutoCallbacks) - new ServiceThread( this).start(); + new Thread(this).start(); } public DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl) @@ -775,7 +832,6 @@ class AppleRegistration extends AppleService implements DNSSDRegistration AppleDNSRecord newRecord = new AppleDNSRecord( this); this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord)); - return newRecord; } @@ -786,48 +842,71 @@ class AppleRegistration extends AppleService implements DNSSDRegistration } // Sets fNativeContext. Returns non-zero on error. - protected native int BeginRegister( int ifIndex, int flags, String serviceName, String regType, + protected native int BeginRegister( int ifIndex, int flags, String serviceName, String regType, String domain, String host, int port, byte[] txtRecord); // Sets fNativeContext. Returns non-zero on error. protected native int AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj); +} + +class AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar +{ + public AppleRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { + super(listener); + this.ThrowOnErr( this.CreateConnection()); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, + int rrclass, byte[] rdata, int ttl) + throws DNSSDException + { + AppleDNSRecord newRecord = new AppleDNSRecord( this); + + this.ThrowOnErr( this.RegisterRecord( flags, ifIndex, fullname, rrtype, rrclass, rdata, ttl, newRecord)); + return newRecord; + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateConnection(); - protected RegisterListener fClient; + // Sets fNativeContext. Returns non-zero on error. + protected native int RegisterRecord( int flags, int ifIndex, String fullname, int rrtype, + int rrclass, byte[] rdata, int ttl, AppleDNSRecord destObj); } class AppleQuery extends AppleService { - public AppleQuery( int flags, int ifIndex, String serviceName, int rrtype, - int rrclass, QueryListener client) + public AppleQuery( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener client) throws DNSSDException - { - fClient = client; + { + super(client); this.ThrowOnErr( this.CreateQuery( flags, ifIndex, serviceName, rrtype, rrclass)); if ( !AppleDNSSD.hasAutoCallbacks) - new ServiceThread( this).start(); + new Thread(this).start(); } // Sets fNativeContext. Returns non-zero on error. protected native int CreateQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass); - - protected QueryListener fClient; } class AppleDomainEnum extends AppleService { - public AppleDomainEnum( int flags, int ifIndex, DomainListener listener) + public AppleDomainEnum( int flags, int ifIndex, DomainListener client) throws DNSSDException - { - fClient = listener; + { + super(client); this.ThrowOnErr( this.BeginEnum( flags, ifIndex)); if ( !AppleDNSSD.hasAutoCallbacks) - new ServiceThread( this).start(); + new Thread(this).start(); } // Sets fNativeContext. Returns non-zero on error. protected native int BeginEnum( int flags, int ifIndex); - - protected DomainListener fClient; }