1 /* -*- Mode: C; tab-width: 4 -*- 2 * 3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 17 This file contains the platform support for DNSSD and related Java classes. 18 It is used to shim through to the underlying <dns_sd.h> API. 19 */ 20 21 // AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response 22 // callbacks automatically (as in the early Windows prototypes). 23 // AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to 24 // invoke response callbacks (as is true on Mac OS X, Posix, Windows, etc.). 25 // (Invoking callbacks automatically on a different thread sounds attractive, but while 26 // the client gains by not needing to add an event source to its main event loop, it loses 27 // by being forced to deal with concurrency and locking, which can be a bigger burden.) 28 #ifndef AUTO_CALLBACKS 29 #define AUTO_CALLBACKS 0 30 #endif 31 32 #if !AUTO_CALLBACKS 33 #ifdef _WIN32 34 #include <winsock2.h> 35 #else //_WIN32 36 #include <sys/types.h> 37 #include <sys/select.h> 38 #endif // _WIN32 39 #endif // AUTO_CALLBACKS 40 41 #include <dns_sd.h> 42 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #ifdef _WIN32 47 #include <winsock2.h> 48 #include <iphlpapi.h> 49 static char * win32_if_indextoname( DWORD ifIndex, char * nameBuff); 50 static DWORD win32_if_nametoindex( const char * nameStr ); 51 #define if_indextoname win32_if_indextoname 52 #define if_nametoindex win32_if_nametoindex 53 #define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH 54 #else // _WIN32 55 #include <sys/socket.h> 56 #include <net/if.h> 57 #endif // _WIN32 58 59 // When compiling with "-Wshadow" set, including jni.h produces the following error: 60 // /System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/jni.h:609: warning: declaration of 'index' shadows a global declaration 61 // To work around this, we use the preprocessor to map the identifier 'index', which appears harmlessly in function prototype declarations, 62 // to something 'jni_index', which doesn't conflict 63 #define index jni_index 64 #include "DNSSD.java.h" 65 #undef index 66 67 //#include <syslog.h> 68 69 // convenience definition 70 #ifdef __GNUC__ 71 #define _UNUSED __attribute__ ((unused)) 72 #else 73 #define _UNUSED 74 #endif 75 76 enum { 77 kInterfaceVersionOne = 1, 78 kInterfaceVersionCurrent // Must match version in .jar file 79 }; 80 81 typedef struct OpContext OpContext; 82 83 struct OpContext 84 { 85 DNSServiceRef ServiceRef; 86 JNIEnv *Env; 87 jobject JavaObj; 88 jobject ClientObj; 89 jmethodID Callback; 90 jmethodID Callback2; 91 }; 92 93 // For AUTO_CALLBACKS, we must attach the callback thread to the Java VM prior to upcall. 94 #if AUTO_CALLBACKS 95 JavaVM *gJavaVM = NULL; 96 #endif 97 98 99 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv, jclass cls, 100 jint callerVersion) 101 { 102 /* Ensure that caller & interface versions match. */ 103 if ( callerVersion != kInterfaceVersionCurrent) 104 return kDNSServiceErr_Incompatible; 105 106 #if AUTO_CALLBACKS 107 { 108 jsize numVMs; 109 110 if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs)) 111 return kDNSServiceErr_BadState; 112 } 113 #endif 114 115 // Set AppleDNSSD.hasAutoCallbacks 116 { 117 #if AUTO_CALLBACKS 118 jboolean hasAutoC = JNI_TRUE; 119 #else 120 jboolean hasAutoC = JNI_FALSE; 121 #endif 122 jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z"); 123 (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC); 124 } 125 126 return kDNSServiceErr_NoError; 127 } 128 129 130 static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str) 131 // Wrapper for JNI GetStringUTFChars() that returns NULL for null str. 132 { 133 return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL; 134 } 135 136 static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff) 137 // Wrapper for JNI GetStringUTFChars() that handles null str. 138 { 139 if ( str != NULL) 140 (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff); 141 } 142 143 144 #if AUTO_CALLBACKS 145 static void SetupCallbackState( JNIEnv **ppEnv) 146 { 147 (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL); 148 } 149 150 static void TeardownCallbackState( void ) 151 { 152 (*gJavaVM)->DetachCurrentThread( gJavaVM); 153 } 154 155 #else // AUTO_CALLBACKS 156 157 static void SetupCallbackState( JNIEnv **ppEnv _UNUSED) 158 { 159 // No setup necessary if ProcessResults() has been called 160 } 161 162 static void TeardownCallbackState( void ) 163 { 164 // No teardown necessary if ProcessResults() has been called 165 } 166 #endif // AUTO_CALLBACKS 167 168 169 static OpContext *NewContext( JNIEnv *pEnv, jobject owner, 170 const char *callbackName, const char *callbackSig) 171 // Create and initialize a new OpContext. 172 { 173 OpContext *pContext = (OpContext*) malloc( sizeof *pContext); 174 175 if ( pContext != NULL) 176 { 177 jfieldID clientField = (*pEnv)->GetFieldID( pEnv, (*pEnv)->GetObjectClass( pEnv, owner), 178 "fListener", "Lcom/apple/dnssd/BaseListener;"); 179 180 pContext->JavaObj = (*pEnv)->NewWeakGlobalRef( pEnv, owner); // must convert local ref to global to cache; 181 pContext->ClientObj = (*pEnv)->GetObjectField( pEnv, owner, clientField); 182 pContext->ClientObj = (*pEnv)->NewWeakGlobalRef( pEnv, pContext->ClientObj); // must convert local ref to global to cache 183 pContext->Callback = (*pEnv)->GetMethodID( pEnv, 184 (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), 185 callbackName, callbackSig); 186 pContext->Callback2 = NULL; // not always used 187 } 188 189 return pContext; 190 } 191 192 193 static void ReportError( JNIEnv *pEnv, jobject target, jobject service, DNSServiceErrorType err) 194 // Invoke operationFailed() method on target with err. 195 { 196 jclass cls = (*pEnv)->GetObjectClass( pEnv, target); 197 jmethodID opFailed = (*pEnv)->GetMethodID( pEnv, cls, "operationFailed", 198 "(Lcom/apple/dnssd/DNSSDService;I)V"); 199 200 (*pEnv)->CallVoidMethod( pEnv, target, opFailed, service, err); 201 } 202 203 JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv *pEnv, jobject pThis) 204 /* Deallocate the dns_sd service browser and set the Java object's fNativeContext field to 0. */ 205 { 206 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 207 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 208 209 if ( contextField != 0) 210 { 211 OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); 212 if ( pContext != NULL) 213 { 214 // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate() 215 (*pEnv)->SetLongField(pEnv, pThis, contextField, 0); 216 if ( pContext->ServiceRef != NULL) 217 DNSServiceRefDeallocate( pContext->ServiceRef); 218 219 (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->JavaObj); 220 (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->ClientObj); 221 free( pContext); 222 } 223 } 224 } 225 226 227 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *pEnv, jobject pThis) 228 /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */ 229 { 230 // BlockForData() not supported with AUTO_CALLBACKS 231 #if !AUTO_CALLBACKS 232 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 233 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 234 235 if ( contextField != 0) 236 { 237 OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); 238 if ( pContext != NULL) 239 { 240 fd_set readFDs; 241 int sd = DNSServiceRefSockFD( pContext->ServiceRef); 242 struct timeval timeout = { 1, 0 }; 243 FD_ZERO( &readFDs); 244 FD_SET( sd, &readFDs); 245 246 // Q: Why do we poll here? 247 // A: Because there's no other thread-safe way to do it. 248 // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not, 249 // and arguably Linux is correct (See <http://www.ussg.iu.edu/hypermail/linux/kernel/0405.1/0418.html>) 250 // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while 251 // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way 252 // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously. 253 // If we try to do this without holding any lock, then right as we jump to the select() routine, 254 // some other thread could stop our operation (thereby closing the socket), 255 // and then that thread (or even some third, unrelated thread) 256 // could do some other DNS-SD operation (or some other operation that opens a new file descriptor) 257 // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor 258 // that may coincidentally have the same numerical value, but is semantically unrelated 259 // to the true file descriptor we thought we were blocking on. 260 // We can't stop this race condition from happening, but at least if we wake up once a second we can detect 261 // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd. 262 263 if (select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout) == 1) return(1); 264 } 265 } 266 #endif // !AUTO_CALLBACKS 267 return(0); 268 } 269 270 271 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv *pEnv, jobject pThis) 272 /* Call through to DNSServiceProcessResult() while data remains on socket. */ 273 { 274 #if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS 275 276 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 277 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 278 OpContext *pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); 279 DNSServiceErrorType err = kDNSServiceErr_BadState; 280 281 if ( pContext != NULL) 282 { 283 int sd = DNSServiceRefSockFD( pContext->ServiceRef); 284 fd_set readFDs; 285 struct timeval zeroTimeout = { 0, 0 }; 286 287 pContext->Env = pEnv; 288 289 FD_ZERO( &readFDs); 290 FD_SET( sd, &readFDs); 291 292 err = kDNSServiceErr_NoError; 293 if (0 < select(sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)) 294 { 295 err = DNSServiceProcessResult(pContext->ServiceRef); 296 // Use caution here! 297 // We cannot touch any data structures associated with this operation! 298 // The DNSServiceProcessResult() routine should have invoked our callback, 299 // and our callback could have terminated the operation with op.stop(); 300 // and that means HaltOperation() will have been called, which frees pContext. 301 // Basically, from here we just have to get out without touching any stale 302 // data structures that could blow up on us! Particularly, any attempt 303 // to loop here reading more results from the file descriptor is unsafe. 304 } 305 } 306 return err; 307 #endif // AUTO_CALLBACKS 308 } 309 310 311 static void DNSSD_API ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, 312 DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, 313 const char *replyDomain, void *context) 314 { 315 OpContext *pContext = (OpContext*) context; 316 317 SetupCallbackState( &pContext->Env); 318 319 if ( pContext->ClientObj != NULL && pContext->Callback != NULL) 320 { 321 if ( errorCode == kDNSServiceErr_NoError) 322 { 323 (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, 324 ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, 325 pContext->JavaObj, flags, interfaceIndex, 326 (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), 327 (*pContext->Env)->NewStringUTF( pContext->Env, regtype), 328 (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); 329 } 330 else 331 ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); 332 } 333 334 TeardownCallbackState(); 335 } 336 337 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv *pEnv, jobject pThis, 338 jint flags, jint ifIndex, jstring regType, jstring domain) 339 { 340 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 341 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 342 OpContext *pContext = NULL; 343 DNSServiceErrorType err = kDNSServiceErr_NoError; 344 345 if ( contextField != 0) 346 pContext = NewContext( pEnv, pThis, "serviceFound", 347 "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); 348 else 349 err = kDNSServiceErr_BadParam; 350 351 if ( pContext != NULL) 352 { 353 const char *regStr = SafeGetUTFChars( pEnv, regType); 354 const char *domainStr = SafeGetUTFChars( pEnv, domain); 355 356 pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, 357 (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), 358 "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); 359 360 err = DNSServiceBrowse( &pContext->ServiceRef, flags, ifIndex, regStr, domainStr, ServiceBrowseReply, pContext); 361 if ( err == kDNSServiceErr_NoError) 362 { 363 (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); 364 } 365 366 SafeReleaseUTFChars( pEnv, regType, regStr); 367 SafeReleaseUTFChars( pEnv, domain, domainStr); 368 } 369 else 370 err = kDNSServiceErr_NoMemory; 371 372 return err; 373 } 374 375 376 static void DNSSD_API ServiceResolveReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, 377 DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, 378 uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) 379 { 380 OpContext *pContext = (OpContext*) context; 381 jclass txtCls; 382 jmethodID txtCtor; 383 jbyteArray txtBytes; 384 jobject txtObj; 385 jbyte *pBytes; 386 387 SetupCallbackState( &pContext->Env); 388 389 txtCls = (*pContext->Env)->FindClass( pContext->Env, "com/apple/dnssd/TXTRecord"); 390 txtCtor = (*pContext->Env)->GetMethodID( pContext->Env, txtCls, "<init>", "([B)V"); 391 392 if ( pContext->ClientObj != NULL && pContext->Callback != NULL && txtCtor != NULL && 393 NULL != ( txtBytes = (*pContext->Env)->NewByteArray( pContext->Env, txtLen))) 394 { 395 if ( errorCode == kDNSServiceErr_NoError) 396 { 397 // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit 398 // pattern into a number here. 399 port = ( ((unsigned char*) &port)[0] << 8) | ((unsigned char*) &port)[1]; 400 401 // Initialize txtBytes with contents of txtRecord 402 pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, txtBytes, NULL); 403 memcpy( pBytes, txtRecord, txtLen); 404 (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, txtBytes, pBytes, JNI_COMMIT); 405 406 // Construct txtObj with txtBytes 407 txtObj = (*pContext->Env)->NewObject( pContext->Env, txtCls, txtCtor, txtBytes); 408 (*pContext->Env)->DeleteLocalRef( pContext->Env, txtBytes); 409 410 (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, 411 pContext->JavaObj, flags, interfaceIndex, 412 (*pContext->Env)->NewStringUTF( pContext->Env, fullname), 413 (*pContext->Env)->NewStringUTF( pContext->Env, hosttarget), 414 port, txtObj); 415 } 416 else 417 ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); 418 } 419 420 TeardownCallbackState(); 421 } 422 423 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv *pEnv, jobject pThis, 424 jint flags, jint ifIndex, jstring serviceName, jstring regType, jstring domain) 425 { 426 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 427 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 428 OpContext *pContext = NULL; 429 DNSServiceErrorType err = kDNSServiceErr_NoError; 430 431 if ( contextField != 0) 432 pContext = NewContext( pEnv, pThis, "serviceResolved", 433 "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V"); 434 else 435 err = kDNSServiceErr_BadParam; 436 437 if ( pContext != NULL) 438 { 439 const char *servStr = SafeGetUTFChars( pEnv, serviceName); 440 const char *regStr = SafeGetUTFChars( pEnv, regType); 441 const char *domainStr = SafeGetUTFChars( pEnv, domain); 442 443 err = DNSServiceResolve( &pContext->ServiceRef, flags, ifIndex, 444 servStr, regStr, domainStr, ServiceResolveReply, pContext); 445 if ( err == kDNSServiceErr_NoError) 446 { 447 (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); 448 } 449 450 SafeReleaseUTFChars( pEnv, serviceName, servStr); 451 SafeReleaseUTFChars( pEnv, regType, regStr); 452 SafeReleaseUTFChars( pEnv, domain, domainStr); 453 } 454 else 455 err = kDNSServiceErr_NoMemory; 456 457 return err; 458 } 459 460 461 static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, 462 DNSServiceErrorType errorCode, const char *serviceName, 463 const char *regType, const char *domain, void *context) 464 { 465 OpContext *pContext = (OpContext*) context; 466 467 SetupCallbackState( &pContext->Env); 468 469 if ( pContext->ClientObj != NULL && pContext->Callback != NULL) 470 { 471 if ( errorCode == kDNSServiceErr_NoError) 472 { 473 (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, 474 pContext->JavaObj, flags, 475 (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), 476 (*pContext->Env)->NewStringUTF( pContext->Env, regType), 477 (*pContext->Env)->NewStringUTF( pContext->Env, domain)); 478 } 479 else 480 ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); 481 } 482 TeardownCallbackState(); 483 } 484 485 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_BeginRegister( JNIEnv *pEnv, jobject pThis, 486 jint ifIndex, jint flags, jstring serviceName, jstring regType, 487 jstring domain, jstring host, jint port, jbyteArray txtRecord) 488 { 489 //syslog(LOG_ERR, "BR"); 490 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 491 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 492 OpContext *pContext = NULL; 493 DNSServiceErrorType err = kDNSServiceErr_NoError; 494 jbyte *pBytes; 495 jsize numBytes; 496 497 //syslog(LOG_ERR, "BR: contextField %d", contextField); 498 499 if ( contextField != 0) 500 pContext = NewContext( pEnv, pThis, "serviceRegistered", 501 "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); 502 else 503 err = kDNSServiceErr_BadParam; 504 505 if ( pContext != NULL) 506 { 507 const char *servStr = SafeGetUTFChars( pEnv, serviceName); 508 const char *regStr = SafeGetUTFChars( pEnv, regType); 509 const char *domainStr = SafeGetUTFChars( pEnv, domain); 510 const char *hostStr = SafeGetUTFChars( pEnv, host); 511 512 //syslog(LOG_ERR, "BR: regStr %s", regStr); 513 514 // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a 515 // big-endian number into a 16-bit pattern here. 516 uint16_t portBits = port; 517 portBits = ( ((unsigned char*) &portBits)[0] << 8) | ((unsigned char*) &portBits)[1]; 518 519 pBytes = txtRecord ? (*pEnv)->GetByteArrayElements( pEnv, txtRecord, NULL) : NULL; 520 numBytes = txtRecord ? (*pEnv)->GetArrayLength( pEnv, txtRecord) : 0; 521 522 err = DNSServiceRegister( &pContext->ServiceRef, flags, ifIndex, servStr, regStr, 523 domainStr, hostStr, portBits, 524 numBytes, pBytes, ServiceRegisterReply, pContext); 525 if ( err == kDNSServiceErr_NoError) 526 { 527 (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); 528 } 529 530 if ( pBytes != NULL) 531 (*pEnv)->ReleaseByteArrayElements( pEnv, txtRecord, pBytes, 0); 532 533 SafeReleaseUTFChars( pEnv, serviceName, servStr); 534 SafeReleaseUTFChars( pEnv, regType, regStr); 535 SafeReleaseUTFChars( pEnv, domain, domainStr); 536 SafeReleaseUTFChars( pEnv, host, hostStr); 537 } 538 else 539 err = kDNSServiceErr_NoMemory; 540 541 return err; 542 } 543 544 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv *pEnv, jobject pThis, 545 jint flags, jint rrType, jbyteArray rData, jint ttl, jobject destObj) 546 { 547 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 548 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 549 jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); 550 jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); 551 OpContext *pContext = NULL; 552 DNSServiceErrorType err = kDNSServiceErr_NoError; 553 jbyte *pBytes; 554 jsize numBytes; 555 DNSRecordRef recRef; 556 557 if ( contextField != 0) 558 pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); 559 if ( pContext == NULL || pContext->ServiceRef == NULL) 560 return kDNSServiceErr_BadParam; 561 562 pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); 563 numBytes = (*pEnv)->GetArrayLength( pEnv, rData); 564 565 err = DNSServiceAddRecord( pContext->ServiceRef, &recRef, flags, rrType, numBytes, pBytes, ttl); 566 if ( err == kDNSServiceErr_NoError) 567 { 568 (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef); 569 } 570 571 if ( pBytes != NULL) 572 (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); 573 574 return err; 575 } 576 577 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis, 578 jint flags, jbyteArray rData, jint ttl) 579 { 580 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 581 jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); 582 jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); 583 OpContext *pContext = NULL; 584 DNSServiceErrorType err = kDNSServiceErr_NoError; 585 jbyte *pBytes; 586 jsize numBytes; 587 DNSRecordRef recRef = NULL; 588 589 if ( ownerField != 0) 590 { 591 jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); 592 jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); 593 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); 594 if ( contextField != 0) 595 pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField); 596 } 597 if ( recField != 0) 598 recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField); 599 if ( pContext == NULL || pContext->ServiceRef == NULL) 600 return kDNSServiceErr_BadParam; 601 602 pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); 603 numBytes = (*pEnv)->GetArrayLength( pEnv, rData); 604 605 err = DNSServiceUpdateRecord( pContext->ServiceRef, recRef, flags, numBytes, pBytes, ttl); 606 607 if ( pBytes != NULL) 608 (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); 609 610 return err; 611 } 612 613 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis) 614 { 615 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 616 jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); 617 jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); 618 OpContext *pContext = NULL; 619 DNSServiceErrorType err = kDNSServiceErr_NoError; 620 DNSRecordRef recRef = NULL; 621 622 if ( ownerField != 0) 623 { 624 jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); 625 jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); 626 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); 627 if ( contextField != 0) 628 pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, ownerObj, contextField); 629 } 630 if ( recField != 0) 631 recRef = (DNSRecordRef) (long) (*pEnv)->GetLongField(pEnv, pThis, recField); 632 if ( pContext == NULL || pContext->ServiceRef == NULL) 633 return kDNSServiceErr_BadParam; 634 635 err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0); 636 637 return err; 638 } 639 640 641 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection( JNIEnv *pEnv, jobject pThis) 642 { 643 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 644 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 645 OpContext *pContext = NULL; 646 DNSServiceErrorType err = kDNSServiceErr_NoError; 647 648 if ( contextField != 0) 649 pContext = NewContext( pEnv, pThis, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V"); 650 else 651 err = kDNSServiceErr_BadParam; 652 653 if ( pContext != NULL) 654 { 655 err = DNSServiceCreateConnection( &pContext->ServiceRef); 656 if ( err == kDNSServiceErr_NoError) 657 { 658 (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); 659 } 660 } 661 else 662 err = kDNSServiceErr_NoMemory; 663 664 return err; 665 } 666 667 struct RecordRegistrationRef 668 { 669 OpContext *Context; 670 jobject RecordObj; 671 }; 672 typedef struct RecordRegistrationRef RecordRegistrationRef; 673 674 static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED, 675 DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags, 676 DNSServiceErrorType errorCode, void *context) 677 { 678 RecordRegistrationRef *regEnvelope = (RecordRegistrationRef*) context; 679 OpContext *pContext = regEnvelope->Context; 680 681 SetupCallbackState( &pContext->Env); 682 683 if ( pContext->ClientObj != NULL && pContext->Callback != NULL) 684 { 685 if ( errorCode == kDNSServiceErr_NoError) 686 { 687 (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, 688 regEnvelope->RecordObj, flags); 689 } 690 else 691 ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); 692 } 693 694 (*pContext->Env)->DeleteWeakGlobalRef( pContext->Env, regEnvelope->RecordObj); 695 free( regEnvelope); 696 697 TeardownCallbackState(); 698 } 699 700 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv *pEnv, jobject pThis, 701 jint flags, jint ifIndex, jstring fullname, jint rrType, jint rrClass, 702 jbyteArray rData, jint ttl, jobject destObj) 703 { 704 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 705 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 706 jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); 707 jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); 708 const char *nameStr = SafeGetUTFChars( pEnv, fullname); 709 OpContext *pContext = NULL; 710 DNSServiceErrorType err = kDNSServiceErr_NoError; 711 jbyte *pBytes; 712 jsize numBytes; 713 DNSRecordRef recRef; 714 RecordRegistrationRef *regEnvelope; 715 716 if ( contextField != 0) 717 pContext = (OpContext*) (long) (*pEnv)->GetLongField(pEnv, pThis, contextField); 718 if ( pContext == NULL || pContext->ServiceRef == NULL || nameStr == NULL) 719 return kDNSServiceErr_BadParam; 720 721 regEnvelope = calloc( 1, sizeof *regEnvelope); 722 if ( regEnvelope == NULL) 723 return kDNSServiceErr_NoMemory; 724 regEnvelope->Context = pContext; 725 regEnvelope->RecordObj = (*pEnv)->NewWeakGlobalRef( pEnv, destObj); // must convert local ref to global to cache 726 727 pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); 728 numBytes = (*pEnv)->GetArrayLength( pEnv, rData); 729 730 err = DNSServiceRegisterRecord( pContext->ServiceRef, &recRef, flags, ifIndex, 731 nameStr, rrType, rrClass, numBytes, pBytes, ttl, 732 RegisterRecordReply, regEnvelope); 733 734 if ( err == kDNSServiceErr_NoError) 735 { 736 (*pEnv)->SetLongField(pEnv, destObj, recField, (long) recRef); 737 } 738 else 739 { 740 if ( regEnvelope->RecordObj != NULL) 741 (*pEnv)->DeleteWeakGlobalRef( pEnv, regEnvelope->RecordObj); 742 free( regEnvelope); 743 } 744 745 if ( pBytes != NULL) 746 (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); 747 748 SafeReleaseUTFChars( pEnv, fullname, nameStr); 749 750 return err; 751 } 752 753 754 static void DNSSD_API ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, 755 DNSServiceErrorType errorCode, const char *serviceName, 756 uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, 757 const void *rdata, uint32_t ttl, void *context) 758 { 759 OpContext *pContext = (OpContext*) context; 760 jbyteArray rDataObj; 761 jbyte *pBytes; 762 763 SetupCallbackState( &pContext->Env); 764 765 if ( pContext->ClientObj != NULL && pContext->Callback != NULL && 766 NULL != ( rDataObj = (*pContext->Env)->NewByteArray( pContext->Env, rdlen))) 767 { 768 if ( errorCode == kDNSServiceErr_NoError) 769 { 770 // Initialize rDataObj with contents of rdata 771 pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, rDataObj, NULL); 772 memcpy( pBytes, rdata, rdlen); 773 (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, rDataObj, pBytes, JNI_COMMIT); 774 775 (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, 776 pContext->JavaObj, flags, interfaceIndex, 777 (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), 778 rrtype, rrclass, rDataObj, ttl); 779 } 780 else 781 ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); 782 } 783 TeardownCallbackState(); 784 } 785 786 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv, jobject pThis, 787 jint flags, jint ifIndex, jstring serviceName, jint rrtype, jint rrclass) 788 { 789 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 790 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 791 OpContext *pContext = NULL; 792 DNSServiceErrorType err = kDNSServiceErr_NoError; 793 794 if ( contextField != 0) 795 pContext = NewContext( pEnv, pThis, "queryAnswered", 796 "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V"); 797 else 798 err = kDNSServiceErr_BadParam; 799 800 if ( pContext != NULL) 801 { 802 const char *servStr = SafeGetUTFChars( pEnv, serviceName); 803 804 err = DNSServiceQueryRecord( &pContext->ServiceRef, flags, ifIndex, servStr, 805 rrtype, rrclass, ServiceQueryReply, pContext); 806 if ( err == kDNSServiceErr_NoError) 807 { 808 (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); 809 } 810 811 SafeReleaseUTFChars( pEnv, serviceName, servStr); 812 } 813 else 814 err = kDNSServiceErr_NoMemory; 815 816 return err; 817 } 818 819 820 static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, 821 DNSServiceErrorType errorCode, const char *replyDomain, void *context) 822 { 823 OpContext *pContext = (OpContext*) context; 824 825 SetupCallbackState( &pContext->Env); 826 827 if ( pContext->ClientObj != NULL && pContext->Callback != NULL) 828 { 829 if ( errorCode == kDNSServiceErr_NoError) 830 { 831 (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, 832 ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, 833 pContext->JavaObj, flags, interfaceIndex, 834 (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); 835 } 836 else 837 ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); 838 } 839 TeardownCallbackState(); 840 } 841 842 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDomainEnum_BeginEnum( JNIEnv *pEnv, jobject pThis, 843 jint flags, jint ifIndex) 844 { 845 jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); 846 jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); 847 OpContext *pContext = NULL; 848 DNSServiceErrorType err = kDNSServiceErr_NoError; 849 850 if ( contextField != 0) 851 pContext = NewContext( pEnv, pThis, "domainFound", 852 "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); 853 else 854 err = kDNSServiceErr_BadParam; 855 856 if ( pContext != NULL) 857 { 858 pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, 859 (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), 860 "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); 861 862 err = DNSServiceEnumerateDomains( &pContext->ServiceRef, flags, ifIndex, 863 DomainEnumReply, pContext); 864 if ( err == kDNSServiceErr_NoError) 865 { 866 (*pEnv)->SetLongField(pEnv, pThis, contextField, (long) pContext); 867 } 868 } 869 else 870 err = kDNSServiceErr_NoMemory; 871 872 return err; 873 } 874 875 876 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_ConstructName( JNIEnv *pEnv, jobject pThis _UNUSED, 877 jstring serviceName, jstring regtype, jstring domain, jobjectArray pOut) 878 { 879 DNSServiceErrorType err = kDNSServiceErr_NoError; 880 const char *nameStr = SafeGetUTFChars( pEnv, serviceName); 881 const char *regStr = SafeGetUTFChars( pEnv, regtype); 882 const char *domStr = SafeGetUTFChars( pEnv, domain); 883 char buff[ kDNSServiceMaxDomainName + 1]; 884 885 err = DNSServiceConstructFullName( buff, nameStr, regStr, domStr); 886 887 if ( err == kDNSServiceErr_NoError) 888 { 889 // pOut is expected to be a String[1] array. 890 (*pEnv)->SetObjectArrayElement( pEnv, pOut, 0, (*pEnv)->NewStringUTF( pEnv, buff)); 891 } 892 893 SafeReleaseUTFChars( pEnv, serviceName, nameStr); 894 SafeReleaseUTFChars( pEnv, regtype, regStr); 895 SafeReleaseUTFChars( pEnv, domain, domStr); 896 897 return err; 898 } 899 900 JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv *pEnv, jobject pThis _UNUSED, 901 jint flags, jint ifIndex, jstring fullName, 902 jint rrtype, jint rrclass, jbyteArray rdata) 903 { 904 jbyte *pBytes; 905 jsize numBytes; 906 const char *nameStr = SafeGetUTFChars( pEnv, fullName); 907 908 pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL); 909 numBytes = (*pEnv)->GetArrayLength( pEnv, rdata); 910 911 DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes); 912 913 if ( pBytes != NULL) 914 (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0); 915 916 SafeReleaseUTFChars( pEnv, fullName, nameStr); 917 } 918 919 #define LOCAL_ONLY_NAME "loo" 920 #define P2P_NAME "p2p" 921 922 JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED, 923 jint ifIndex) 924 { 925 char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE]; 926 927 if (ifIndex == (jint) kDNSServiceInterfaceIndexP2P) 928 p = P2P_NAME; 929 else if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly) 930 p = if_indextoname( ifIndex, nameBuff ); 931 932 return (*pEnv)->NewStringUTF( pEnv, p); 933 } 934 935 936 JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED, 937 jstring ifName) 938 { 939 uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly; 940 const char *nameStr = SafeGetUTFChars( pEnv, ifName); 941 942 if (strcmp(nameStr, P2P_NAME) == 0) 943 ifIndex = kDNSServiceInterfaceIndexP2P; 944 else if (strcmp(nameStr, LOCAL_ONLY_NAME)) 945 ifIndex = if_nametoindex( nameStr); 946 947 SafeReleaseUTFChars( pEnv, ifName, nameStr); 948 949 return ifIndex; 950 } 951 952 953 #if defined(_WIN32) 954 static char* 955 win32_if_indextoname( DWORD ifIndex, char * nameBuff) 956 { 957 PIP_ADAPTER_INFO pAdapterInfo = NULL; 958 PIP_ADAPTER_INFO pAdapter = NULL; 959 DWORD dwRetVal = 0; 960 char * ifName = NULL; 961 ULONG ulOutBufLen = 0; 962 963 if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) 964 { 965 goto exit; 966 } 967 968 pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); 969 970 if (pAdapterInfo == NULL) 971 { 972 goto exit; 973 } 974 975 dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen ); 976 977 if (dwRetVal != NO_ERROR) 978 { 979 goto exit; 980 } 981 982 pAdapter = pAdapterInfo; 983 while (pAdapter) 984 { 985 if (pAdapter->Index == ifIndex) 986 { 987 // It would be better if we passed in the length of nameBuff to this 988 // function, so we would have absolute certainty that no buffer 989 // overflows would occur. Buffer overflows *shouldn't* occur because 990 // nameBuff is of size MAX_ADAPTER_NAME_LENGTH. 991 strcpy( nameBuff, pAdapter->AdapterName ); 992 ifName = nameBuff; 993 break; 994 } 995 996 pAdapter = pAdapter->Next; 997 } 998 999 exit: 1000 1001 if (pAdapterInfo != NULL) 1002 { 1003 free( pAdapterInfo ); 1004 pAdapterInfo = NULL; 1005 } 1006 1007 return ifName; 1008 } 1009 1010 1011 static DWORD 1012 win32_if_nametoindex( const char * nameStr ) 1013 { 1014 PIP_ADAPTER_INFO pAdapterInfo = NULL; 1015 PIP_ADAPTER_INFO pAdapter = NULL; 1016 DWORD dwRetVal = 0; 1017 DWORD ifIndex = 0; 1018 ULONG ulOutBufLen = 0; 1019 1020 if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) 1021 { 1022 goto exit; 1023 } 1024 1025 pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); 1026 1027 if (pAdapterInfo == NULL) 1028 { 1029 goto exit; 1030 } 1031 1032 dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen ); 1033 1034 if (dwRetVal != NO_ERROR) 1035 { 1036 goto exit; 1037 } 1038 1039 pAdapter = pAdapterInfo; 1040 while (pAdapter) 1041 { 1042 if (strcmp(pAdapter->AdapterName, nameStr) == 0) 1043 { 1044 ifIndex = pAdapter->Index; 1045 break; 1046 } 1047 1048 pAdapter = pAdapter->Next; 1049 } 1050 1051 exit: 1052 1053 if (pAdapterInfo != NULL) 1054 { 1055 free( pAdapterInfo ); 1056 pAdapterInfo = NULL; 1057 } 1058 1059 return ifIndex; 1060 } 1061 #endif 1062 1063 1064 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion 1065 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" 1066 // To expand "version" to its value before making the string, use STRINGIFY(version) instead 1067 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s 1068 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) 1069 1070 // NOT static -- otherwise the compiler may optimize it out 1071 // The "@(#) " pattern is a special prefix the "what" command looks for 1072 #ifndef MDNS_VERSIONSTR_NODTS 1073 const char VersionString_SCCS[] = "@(#) libjdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; 1074 #else 1075 const char VersionString_SCCS[] = "@(#) libjdns_sd " STRINGIFY(mDNSResponderVersion); 1076 #endif