/* -*- 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 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 #else //_WIN32 #include #include #endif // _WIN32 #endif // AUTO_CALLBACKS #include #include #include #include #ifdef _WIN32 #include #include 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 #include #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 // 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 ) // 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, "", "([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__ ")";