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