summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSShared/Java/JNISupport.c
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/mDNSShared/Java/JNISupport.c')
-rw-r--r--mDNSResponder/mDNSShared/Java/JNISupport.c1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSShared/Java/JNISupport.c b/mDNSResponder/mDNSShared/Java/JNISupport.c
new file mode 100644
index 00000000..22b40930
--- /dev/null
+++ b/mDNSResponder/mDNSShared/Java/JNISupport.c
@@ -0,0 +1,1072 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * 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.
+
+ This file contains the platform support for DNSSD and related Java classes.
+ It is used to shim through to the underlying <dns_sd.h> API.
+ */
+
+// AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response
+// callbacks automatically (as in the early Windows prototypes).
+// AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to
+// invoke response callbacks (as is true on Mac OS X, Posix, Windows, etc.).
+// (Invoking callbacks automatically on a different thread sounds attractive, but while
+// the client gains by not needing to add an event source to its main event loop, it loses
+// by being forced to deal with concurrency and locking, which can be a bigger burden.)
+#ifndef AUTO_CALLBACKS
+#define AUTO_CALLBACKS 0
+#endif
+
+#if !AUTO_CALLBACKS
+#ifdef _WIN32
+#include <winsock2.h>
+#else //_WIN32
+#include <sys/types.h>
+#include <sys/select.h>
+#endif // _WIN32
+#endif // AUTO_CALLBACKS
+
+#include <dns_sd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <iphlpapi.h>
+static char * win32_if_indextoname( DWORD ifIndex, char * nameBuff);
+static DWORD win32_if_nametoindex( const char * nameStr );
+#define if_indextoname win32_if_indextoname
+#define if_nametoindex win32_if_nametoindex
+#define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH
+#else // _WIN32
+#include <sys/socket.h>
+#include <net/if.h>
+#endif // _WIN32
+
+// When compiling with "-Wshadow" set, including jni.h produces the following error:
+// /System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/jni.h:609: warning: declaration of 'index' shadows a global declaration
+// To work around this, we use the preprocessor to map the identifier 'index', which appears harmlessly in function prototype declarations,
+// to something 'jni_index', which doesn't conflict
+#define index jni_index
+#include "DNSSD.java.h"
+#undef index
+
+//#include <syslog.h>
+
+// convenience definition
+#ifdef __GNUC__
+#define _UNUSED __attribute__ ((unused))
+#else
+#define _UNUSED
+#endif
+
+enum {
+ kInterfaceVersionOne = 1,
+ kInterfaceVersionCurrent // Must match version in .jar file
+};
+
+typedef struct OpContext OpContext;
+
+struct OpContext
+{
+ DNSServiceRef ServiceRef;
+ JNIEnv *Env;
+ jobject JavaObj;
+ jobject ClientObj;
+ jmethodID Callback;
+ jmethodID Callback2;
+};
+
+// For AUTO_CALLBACKS, we must attach the callback thread to the Java VM prior to upcall.
+#if AUTO_CALLBACKS
+JavaVM *gJavaVM = NULL;
+#endif
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv, jclass cls,
+ jint callerVersion)
+{
+ /* Ensure that caller & interface versions match. */
+ if ( callerVersion != kInterfaceVersionCurrent)
+ return kDNSServiceErr_Incompatible;
+
+#if AUTO_CALLBACKS
+ {
+ jsize numVMs;
+
+ if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs))
+ return kDNSServiceErr_BadState;
+ }
+#endif
+
+ // Set AppleDNSSD.hasAutoCallbacks
+ {
+#if AUTO_CALLBACKS
+ jboolean hasAutoC = JNI_TRUE;
+#else
+ jboolean hasAutoC = JNI_FALSE;
+#endif
+ jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z");
+ (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC);
+ }
+
+ return kDNSServiceErr_NoError;
+}
+
+
+static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str)
+// Wrapper for JNI GetStringUTFChars() that returns NULL for null str.
+{
+ return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL;
+}
+
+static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff)
+// Wrapper for JNI GetStringUTFChars() that handles null str.
+{
+ if ( str != NULL)
+ (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff);
+}
+
+
+#if AUTO_CALLBACKS
+static void SetupCallbackState( JNIEnv **ppEnv)
+{
+ (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL);
+}
+
+static void TeardownCallbackState( void )
+{
+ (*gJavaVM)->DetachCurrentThread( gJavaVM);
+}
+
+#else // AUTO_CALLBACKS
+
+static void SetupCallbackState( JNIEnv **ppEnv _UNUSED)
+{
+ // No setup necessary if ProcessResults() has been called
+}
+
+static void TeardownCallbackState( void )
+{
+ // No teardown necessary if ProcessResults() has been called
+}
+#endif // AUTO_CALLBACKS
+
+
+static OpContext *NewContext( JNIEnv *pEnv, jobject owner,
+ const char *callbackName, const char *callbackSig)
+// Create and initialize a new OpContext.
+{
+ OpContext *pContext = (OpContext*) malloc( sizeof *pContext);
+
+ if ( pContext != NULL)
+ {
+ jfieldID clientField = (*pEnv)->GetFieldID( pEnv, (*pEnv)->GetObjectClass( pEnv, owner),
+ "fListener", "Lcom/apple/dnssd/BaseListener;");
+
+ pContext->JavaObj = (*pEnv)->NewWeakGlobalRef( pEnv, owner); // must convert local ref to global to cache;
+ pContext->ClientObj = (*pEnv)->GetObjectField( pEnv, owner, clientField);
+ pContext->ClientObj = (*pEnv)->NewWeakGlobalRef( pEnv, pContext->ClientObj); // must convert local ref to global to cache
+ pContext->Callback = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ callbackName, callbackSig);
+ pContext->Callback2 = NULL; // not always used
+ }
+
+ return pContext;
+}
+
+
+static void ReportError( JNIEnv *pEnv, jobject target, jobject service, DNSServiceErrorType err)
+// Invoke operationFailed() method on target with err.
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, target);
+ jmethodID opFailed = (*pEnv)->GetMethodID( pEnv, cls, "operationFailed",
+ "(Lcom/apple/dnssd/DNSSDService;I)V");
+
+ (*pEnv)->CallVoidMethod( pEnv, target, opFailed, service, err);
+}
+
+JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv *pEnv, jobject pThis)
+/* Deallocate the dns_sd service browser and set the Java object's fNativeContext field to 0. */
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+
+ if ( contextField != 0)
+ {
+ OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ if ( pContext != NULL)
+ {
+ // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate()
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, 0);
+ if ( pContext->ServiceRef != NULL)
+ DNSServiceRefDeallocate( pContext->ServiceRef);
+
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->JavaObj);
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->ClientObj);
+ free( pContext);
+ }
+ }
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *pEnv, jobject pThis)
+/* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
+{
+// BlockForData() not supported with AUTO_CALLBACKS
+#if !AUTO_CALLBACKS
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+
+ if ( contextField != 0)
+ {
+ OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ if ( pContext != NULL)
+ {
+ fd_set readFDs;
+ int sd = DNSServiceRefSockFD( pContext->ServiceRef);
+ struct timeval timeout = { 1, 0 };
+ FD_ZERO( &readFDs);
+ FD_SET( sd, &readFDs);
+
+ // Q: Why do we poll here?
+ // A: Because there's no other thread-safe way to do it.
+ // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not,
+ // and arguably Linux is correct (See <http://www.ussg.iu.edu/hypermail/linux/kernel/0405.1/0418.html>)
+ // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while
+ // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way
+ // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously.
+ // If we try to do this without holding any lock, then right as we jump to the select() routine,
+ // some other thread could stop our operation (thereby closing the socket),
+ // and then that thread (or even some third, unrelated thread)
+ // could do some other DNS-SD operation (or some other operation that opens a new file descriptor)
+ // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor
+ // that may coincidentally have the same numerical value, but is semantically unrelated
+ // to the true file descriptor we thought we were blocking on.
+ // We can't stop this race condition from happening, but at least if we wake up once a second we can detect
+ // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd.
+
+ if (select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout) == 1) return(1);
+ }
+ }
+#endif // !AUTO_CALLBACKS
+ return(0);
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv *pEnv, jobject pThis)
+/* Call through to DNSServiceProcessResult() while data remains on socket. */
+{
+#if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS
+
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ DNSServiceErrorType err = kDNSServiceErr_BadState;
+
+ if ( pContext != NULL)
+ {
+ int sd = DNSServiceRefSockFD( pContext->ServiceRef);
+ fd_set readFDs;
+ struct timeval zeroTimeout = { 0, 0 };
+
+ pContext->Env = pEnv;
+
+ FD_ZERO( &readFDs);
+ FD_SET( sd, &readFDs);
+
+ err = kDNSServiceErr_NoError;
+ if (0 < select(sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout))
+ {
+ err = DNSServiceProcessResult(pContext->ServiceRef);
+ // Use caution here!
+ // We cannot touch any data structures associated with this operation!
+ // The DNSServiceProcessResult() routine should have invoked our callback,
+ // and our callback could have terminated the operation with op.stop();
+ // and that means HaltOperation() will have been called, which frees pContext.
+ // Basically, from here we just have to get out without touching any stale
+ // data structures that could blow up on us! Particularly, any attempt
+ // to loop here reading more results from the file descriptor is unsafe.
+ }
+ }
+ return err;
+#endif // AUTO_CALLBACKS
+}
+
+
+static void DNSSD_API ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *serviceName, const char *regtype,
+ const char *replyDomain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj,
+ ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ (*pContext->Env)->NewStringUTF( pContext->Env, regtype),
+ (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring regType, jstring domain)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceFound",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+
+ pContext->Callback2 = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+
+ err = DNSServiceBrowse( &pContext->ServiceRef, flags, ifIndex, regStr, domainStr, ServiceBrowseReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceResolveReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget,
+ uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+ jclass txtCls;
+ jmethodID txtCtor;
+ jbyteArray txtBytes;
+ jobject txtObj;
+ jbyte *pBytes;
+
+ SetupCallbackState( &pContext->Env);
+
+ txtCls = (*pContext->Env)->FindClass( pContext->Env, "com/apple/dnssd/TXTRecord");
+ txtCtor = (*pContext->Env)->GetMethodID( pContext->Env, txtCls, "<init>", "([B)V");
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL && txtCtor != NULL &&
+ NULL != ( txtBytes = (*pContext->Env)->NewByteArray( pContext->Env, txtLen)))
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit
+ // pattern into a number here.
+ port = ( ((unsigned char*) &port)[0] << 8) | ((unsigned char*) &port)[1];
+
+ // Initialize txtBytes with contents of txtRecord
+ pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, txtBytes, NULL);
+ memcpy( pBytes, txtRecord, txtLen);
+ (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, txtBytes, pBytes, JNI_COMMIT);
+
+ // Construct txtObj with txtBytes
+ txtObj = (*pContext->Env)->NewObject( pContext->Env, txtCls, txtCtor, txtBytes);
+ (*pContext->Env)->DeleteLocalRef( pContext->Env, txtBytes);
+
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, fullname),
+ (*pContext->Env)->NewStringUTF( pContext->Env, hosttarget),
+ port, txtObj);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring serviceName, jstring regType, jstring domain)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceResolved",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+
+ err = DNSServiceResolve( &pContext->ServiceRef, flags, ifIndex,
+ servStr, regStr, domainStr, ServiceResolveReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, const char *serviceName,
+ const char *regType, const char *domain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ (*pContext->Env)->NewStringUTF( pContext->Env, regType),
+ (*pContext->Env)->NewStringUTF( pContext->Env, domain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_BeginRegister( JNIEnv *pEnv, jobject pThis,
+ jint ifIndex, jint flags, jstring serviceName, jstring regType,
+ jstring domain, jstring host, jint port, jbyteArray txtRecord)
+{
+ //syslog(LOG_ERR, "BR");
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+
+ //syslog(LOG_ERR, "BR: contextField %d", contextField);
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceRegistered",
+ "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+ const char *hostStr = SafeGetUTFChars( pEnv, host);
+
+ //syslog(LOG_ERR, "BR: regStr %s", regStr);
+
+ // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a
+ // big-endian number into a 16-bit pattern here.
+ uint16_t portBits = port;
+ portBits = ( ((unsigned char*) &portBits)[0] << 8) | ((unsigned char*) &portBits)[1];
+
+ pBytes = txtRecord ? (*pEnv)->GetByteArrayElements( pEnv, txtRecord, NULL) : NULL;
+ numBytes = txtRecord ? (*pEnv)->GetArrayLength( pEnv, txtRecord) : 0;
+
+ err = DNSServiceRegister( &pContext->ServiceRef, flags, ifIndex, servStr, regStr,
+ domainStr, hostStr, portBits,
+ numBytes, pBytes, ServiceRegisterReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, txtRecord, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ SafeReleaseUTFChars( pEnv, host, hostStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint rrType, jbyteArray rData, jint ttl, jobject destObj)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj);
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef;
+
+ if ( contextField != 0)
+ pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceAddRecord( pContext->ServiceRef, &recRef, flags, rrType, numBytes, pBytes, ttl);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis,
+ jint flags, jbyteArray rData, jint ttl)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;");
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef = NULL;
+
+ if ( ownerField != 0)
+ {
+ jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField);
+ jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J");
+ if ( contextField != 0)
+ pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField);
+ }
+ if ( recField != 0)
+ recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceUpdateRecord( pContext->ServiceRef, recRef, flags, numBytes, pBytes, ttl);
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;");
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ DNSRecordRef recRef = NULL;
+
+ if ( ownerField != 0)
+ {
+ jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField);
+ jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J");
+ if ( contextField != 0)
+ pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField);
+ }
+ if ( recField != 0)
+ recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0);
+
+ return err;
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection( JNIEnv *pEnv, jobject pThis)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ err = DNSServiceCreateConnection( &pContext->ServiceRef);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+struct RecordRegistrationRef
+{
+ OpContext *Context;
+ jobject RecordObj;
+};
+typedef struct RecordRegistrationRef RecordRegistrationRef;
+
+static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED,
+ DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, void *context)
+{
+ RecordRegistrationRef *regEnvelope = (RecordRegistrationRef*) context;
+ OpContext *pContext = regEnvelope->Context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ regEnvelope->RecordObj, flags);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ (*pContext->Env)->DeleteWeakGlobalRef( pContext->Env, regEnvelope->RecordObj);
+ free( regEnvelope);
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring fullname, jint rrType, jint rrClass,
+ jbyteArray rData, jint ttl, jobject destObj)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj);
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J");
+ const char *nameStr = SafeGetUTFChars( pEnv, fullname);
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef;
+ RecordRegistrationRef *regEnvelope;
+
+ if ( contextField != 0)
+ pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL || nameStr == NULL)
+ return kDNSServiceErr_BadParam;
+
+ regEnvelope = calloc( 1, sizeof *regEnvelope);
+ if ( regEnvelope == NULL)
+ return kDNSServiceErr_NoMemory;
+ regEnvelope->Context = pContext;
+ regEnvelope->RecordObj = (*pEnv)->NewWeakGlobalRef( pEnv, destObj); // must convert local ref to global to cache
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceRegisterRecord( pContext->ServiceRef, &recRef, flags, ifIndex,
+ nameStr, rrType, rrClass, numBytes, pBytes, ttl,
+ RegisterRecordReply, regEnvelope);
+
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef);
+ }
+ else
+ {
+ if ( regEnvelope->RecordObj != NULL)
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, regEnvelope->RecordObj);
+ free( regEnvelope);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, fullname, nameStr);
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *serviceName,
+ uint16_t rrtype, uint16_t rrclass, uint16_t rdlen,
+ const void *rdata, uint32_t ttl, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+ jbyteArray rDataObj;
+ jbyte *pBytes;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL &&
+ NULL != ( rDataObj = (*pContext->Env)->NewByteArray( pContext->Env, rdlen)))
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ // Initialize rDataObj with contents of rdata
+ pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, rDataObj, NULL);
+ memcpy( pBytes, rdata, rdlen);
+ (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, rDataObj, pBytes, JNI_COMMIT);
+
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ rrtype, rrclass, rDataObj, ttl);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring serviceName, jint rrtype, jint rrclass)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "queryAnswered",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+
+ err = DNSServiceQueryRecord( &pContext->ServiceRef, flags, ifIndex, servStr,
+ rrtype, rrclass, ServiceQueryReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *replyDomain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj,
+ ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDomainEnum_BeginEnum( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "domainFound",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ pContext->Callback2 = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V");
+
+ err = DNSServiceEnumerateDomains( &pContext->ServiceRef, flags, ifIndex,
+ DomainEnumReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext);
+ }
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_ConstructName( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jstring serviceName, jstring regtype, jstring domain, jobjectArray pOut)
+{
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ const char *nameStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regtype);
+ const char *domStr = SafeGetUTFChars( pEnv, domain);
+ char buff[ kDNSServiceMaxDomainName + 1];
+
+ err = DNSServiceConstructFullName( buff, nameStr, regStr, domStr);
+
+ if ( err == kDNSServiceErr_NoError)
+ {
+ // pOut is expected to be a String[1] array.
+ (*pEnv)->SetObjectArrayElement( pEnv, pOut, 0, (*pEnv)->NewStringUTF( pEnv, buff));
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, nameStr);
+ SafeReleaseUTFChars( pEnv, regtype, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domStr);
+
+ return err;
+}
+
+JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jint flags, jint ifIndex, jstring fullName,
+ jint rrtype, jint rrclass, jbyteArray rdata)
+{
+ jbyte *pBytes;
+ jsize numBytes;
+ const char *nameStr = SafeGetUTFChars( pEnv, fullName);
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rdata);
+
+ DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes);
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, fullName, nameStr);
+}
+
+#define LOCAL_ONLY_NAME "loo"
+#define P2P_NAME "p2p"
+
+JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jint ifIndex)
+{
+ char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE];
+
+ if (ifIndex == (jint) kDNSServiceInterfaceIndexP2P)
+ p = P2P_NAME;
+ else if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly)
+ p = if_indextoname( ifIndex, nameBuff );
+
+ return (*pEnv)->NewStringUTF( pEnv, p);
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jstring ifName)
+{
+ uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly;
+ const char *nameStr = SafeGetUTFChars( pEnv, ifName);
+
+ if (strcmp(nameStr, P2P_NAME) == 0)
+ ifIndex = kDNSServiceInterfaceIndexP2P;
+ else if (strcmp(nameStr, LOCAL_ONLY_NAME))
+ ifIndex = if_nametoindex( nameStr);
+
+ SafeReleaseUTFChars( pEnv, ifName, nameStr);
+
+ return ifIndex;
+}
+
+
+#if defined(_WIN32)
+static char*
+win32_if_indextoname( DWORD ifIndex, char * nameBuff)
+{
+ PIP_ADAPTER_INFO pAdapterInfo = NULL;
+ PIP_ADAPTER_INFO pAdapter = NULL;
+ DWORD dwRetVal = 0;
+ char * ifName = NULL;
+ ULONG ulOutBufLen = 0;
+
+ if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW)
+ {
+ goto exit;
+ }
+
+ pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
+
+ if (pAdapterInfo == NULL)
+ {
+ goto exit;
+ }
+
+ dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen );
+
+ if (dwRetVal != NO_ERROR)
+ {
+ goto exit;
+ }
+
+ pAdapter = pAdapterInfo;
+ while (pAdapter)
+ {
+ if (pAdapter->Index == ifIndex)
+ {
+ // It would be better if we passed in the length of nameBuff to this
+ // function, so we would have absolute certainty that no buffer
+ // overflows would occur. Buffer overflows *shouldn't* occur because
+ // nameBuff is of size MAX_ADAPTER_NAME_LENGTH.
+ strcpy( nameBuff, pAdapter->AdapterName );
+ ifName = nameBuff;
+ break;
+ }
+
+ pAdapter = pAdapter->Next;
+ }
+
+exit:
+
+ if (pAdapterInfo != NULL)
+ {
+ free( pAdapterInfo );
+ pAdapterInfo = NULL;
+ }
+
+ return ifName;
+}
+
+
+static DWORD
+win32_if_nametoindex( const char * nameStr )
+{
+ PIP_ADAPTER_INFO pAdapterInfo = NULL;
+ PIP_ADAPTER_INFO pAdapter = NULL;
+ DWORD dwRetVal = 0;
+ DWORD ifIndex = 0;
+ ULONG ulOutBufLen = 0;
+
+ if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW)
+ {
+ goto exit;
+ }
+
+ pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
+
+ if (pAdapterInfo == NULL)
+ {
+ goto exit;
+ }
+
+ dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen );
+
+ if (dwRetVal != NO_ERROR)
+ {
+ goto exit;
+ }
+
+ pAdapter = pAdapterInfo;
+ while (pAdapter)
+ {
+ if (strcmp(pAdapter->AdapterName, nameStr) == 0)
+ {
+ ifIndex = pAdapter->Index;
+ break;
+ }
+
+ pAdapter = pAdapter->Next;
+ }
+
+exit:
+
+ if (pAdapterInfo != NULL)
+ {
+ free( pAdapterInfo );
+ pAdapterInfo = NULL;
+ }
+
+ return ifIndex;
+}
+#endif
+
+
+// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
+// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
+// To expand "version" to its value before making the string, use STRINGIFY(version) instead
+#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
+#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
+
+// NOT static -- otherwise the compiler may optimize it out
+// The "@(#) " pattern is a special prefix the "what" command looks for
+const char VersionString_SCCS[] = "@(#) libjdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";