*
* @APPLE_LICENSE_HEADER_START@
*
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
- *
* 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
Change History (most recent first):
$Log: DNSSD.java,v $
+Revision 1.9 2005/10/26 01:52:24 cheshire
+<rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux)
+
+Revision 1.8 2005/07/11 01:55:21 cheshire
+<rdar://problem/4175511> Race condition in Java API
+
+Revision 1.7 2005/07/05 13:01:52 cheshire
+<rdar://problem/4169791> 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
+<rdar://problem/3907498> Java DNSRecord API should be cleaned up
+
+Revision 1.3 2004/11/12 03:23:08 rpantos
+rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex.
+
Revision 1.2 2004/05/20 17:43:18 cheshire
Fix invalid UTF-8 characters in file
"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"
};
if ( fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length))
protected String _getNameForIfIndex( int ifIndex)
{
- return null; // ••Fix me - RNP
+ return GetNameForIfIndex( ifIndex);
}
protected int _getIfIndexForName( String ifName)
{
- return 0; // ••Fix me - RNP
+ return GetIfIndexForName( ifName);
}
protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
int rrclass, byte[] rdata);
+ protected native String GetNameForIfIndex( int ifIndex);
+
+ protected native int GetIfIndexForName( String ifName);
+
protected static native int InitLibrary( int callerVersion);
}
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
{
}
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; }
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;
}
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
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,
String domain);
-
- protected ResolveListener fClient;
}
// An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord.
-class AppleDNSRecord extends DNSRecord
+class AppleDNSRecord implements DNSRecord
{
- public int fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ?
+ public AppleDNSRecord( AppleService owner)
+ {
+ fOwner = owner;
+ fRecord = 0; // record always starts out empty
+ }
+
+ public void update( int flags, byte[] rData, int ttl)
+ throws DNSSDException
+ {
+ this.ThrowOnErr( this.Update( flags, rData, ttl));
+ }
+
+ public void remove()
+ throws DNSSDException
+ {
+ this.ThrowOnErr( this.Remove());
+ }
+
+ protected int fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ?
+ protected AppleService fOwner;
+
+ protected void ThrowOnErr( int rc) throws DNSSDException
+ {
+ if ( rc != 0)
+ throw new AppleDNSSDException( rc);
+ }
+
+ protected native int Update( int flags, byte[] rData, int ttl);
+
+ protected native int Remove();
}
class AppleRegistration extends AppleService implements DNSSDRegistration
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)
throws DNSSDException
{
- AppleDNSRecord newRecord = new AppleDNSRecord();
+ AppleDNSRecord newRecord = new AppleDNSRecord( this);
this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord));
return newRecord;
}
- public void updateRecord( DNSRecord record, int flags, byte[] rData, int ttl)
+ public DNSRecord getTXTRecord()
throws DNSSDException
{
- this.ThrowOnErr( this.UpdateRecord( (AppleDNSRecord) record, flags, rData, ttl));
- }
-
- public void removeRecord( DNSRecord record, int flags)
- throws DNSSDException
- {
- this.ThrowOnErr( this.RemoveRecord( (AppleDNSRecord) record, flags));
+ return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record
}
// Sets fNativeContext. Returns non-zero on error.
// Sets fNativeContext. Returns non-zero on error.
protected native int AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj);
-
- protected native int UpdateRecord( AppleDNSRecord destObj, int flags, byte[] rData, int ttl);
-
- protected native int RemoveRecord( AppleDNSRecord destObj, int flags);
-
- protected RegisterListener fClient;
}
class AppleQuery extends AppleService
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;
}