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