1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 Change History (most recent first):
19 $Log: JNISupport.c,v $
20 Revision 1.22 2007/11/30 23:38:53 cheshire
22 /System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/jni.h:609: warning: declaration of 'index' shadows a global declaration
24 Revision 1.21 2007/09/18 19:09:02 cheshire
25 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
27 Revision 1.20 2007/03/13 01:41:46 cheshire
28 Fixed compile warnings when building 32-bit
30 Revision 1.19 2007/03/13 00:28:03 vazquez
31 <rdar://problem/4625928> Java: Rename exported symbols in libjdns_sd.jnilib
33 Revision 1.18 2007/03/13 00:10:14 vazquez
34 <rdar://problem/4455206> Java: 64 bit JNI patch
36 Revision 1.17 2006/08/14 23:25:08 cheshire
37 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
39 Revision 1.16 2006/07/14 02:35:47 cheshire
40 Added (commented out) syslog debugging messages
42 Revision 1.15 2006/06/27 19:34:43 cheshire
43 <rdar://problem/4430023> txtRecord parameter of DNSServiceResolveReply() should be unsigned char *
45 Revision 1.14 2006/06/20 23:03:35 rpantos
46 <rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent
48 Revision 1.13 2005/10/26 01:52:24 cheshire
49 <rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux)
51 Revision 1.12 2005/07/13 19:20:32 cheshire
52 <rdar://problem/4175511> Race condition in Java API
53 Additional cleanup suggested by Roger -- NewContext() doesn't need ownerClass parameter any more
55 Revision 1.11 2005/07/11 01:55:21 cheshire
56 <rdar://problem/4175511> Race condition in Java API
58 Revision 1.10 2005/07/05 13:01:52 cheshire
59 <rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time
61 Revision 1.9 2004/12/11 03:01:00 rpantos
62 <rdar://problem/3907498> Java DNSRecord API should be cleaned up
64 Revision 1.8 2004/11/30 23:51:05 cheshire
65 Remove double semicolons
67 Revision 1.7 2004/11/23 08:12:04 shersche
68 Implement if_nametoindex and if_indextoname for Win32 platforms
70 Revision 1.6 2004/11/23 03:41:14 cheshire
71 Change JNISupport.c to call if_indextoname & if_nametoindex directly.
72 (May require some additional glue code to work on Windows.)
74 Revision 1.5 2004/11/17 17:07:44 cheshire
75 Updated comment about AUTO_CALLBACKS
77 Revision 1.4 2004/11/12 03:23:09 rpantos
78 rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex.
80 Revision 1.3 2004/06/18 04:44:17 rpantos
81 Adapt to API unification on Windows
83 Revision 1.2 2004/05/28 23:34:42 ksekar
84 <rdar://problem/3672903>: Java project build errors
86 Revision 1.1 2004/04/30 16:29:35 rpantos
90 This file contains the platform support for DNSSD and related Java classes.
91 It is used to shim through to the underlying <dns_sd.h> API.
94 // AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response
95 // callbacks automatically (as in the early Windows prototypes).
96 // AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to
97 // invoke response callbacks (as is true on Mac OS X, Posix, Windows, etc.).
98 // (Invoking callbacks automatically on a different thread sounds attractive, but while
99 // the client gains by not needing to add an event source to its main event loop, it loses
100 // by being forced to deal with concurrency and locking, which can be a bigger burden.)
101 #ifndef AUTO_CALLBACKS
102 #define AUTO_CALLBACKS 0
107 #include <winsock2.h>
109 #include <sys/types.h>
110 #include <sys/select.h>
112 #endif // AUTO_CALLBACKS
120 #include <winsock2.h>
121 #include <iphlpapi.h>
122 static char * if_indextoname( DWORD ifIndex
, char * nameBuff
);
123 static DWORD
if_nametoindex( const char * nameStr
);
124 #define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH
126 #include <sys/socket.h>
130 // When compiling with "-Wshadow" set, including jni.h produces the following error:
131 // /System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/jni.h:609: warning: declaration of 'index' shadows a global declaration
132 // To work around this, we use the preprocessor to map the identifier 'index', which appears harmlessly in function prototype declarations,
133 // to something 'jni_index', which doesn't conflict
134 #define index jni_index
135 #include "DNSSD.java.h"
138 //#include <syslog.h>
140 // convenience definition
142 #define _UNUSED __attribute__ ((unused))
148 kInterfaceVersionOne
= 1,
149 kInterfaceVersionCurrent
// Must match version in .jar file
152 typedef struct OpContext OpContext
;
156 DNSServiceRef ServiceRef
;
164 // For AUTO_CALLBACKS, we must attach the callback thread to the Java VM prior to upcall.
166 JavaVM
*gJavaVM
= NULL
;
170 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv
*pEnv
, jclass cls
,
173 /* Ensure that caller & interface versions match. */
174 if ( callerVersion
!= kInterfaceVersionCurrent
)
175 return kDNSServiceErr_Incompatible
;
181 if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM
, 1, &numVMs
))
182 return kDNSServiceErr_BadState
;
186 // Set AppleDNSSD.hasAutoCallbacks
189 jboolean hasAutoC
= JNI_TRUE
;
191 jboolean hasAutoC
= JNI_FALSE
;
193 jfieldID hasAutoCField
= (*pEnv
)->GetStaticFieldID( pEnv
, cls
, "hasAutoCallbacks", "Z");
194 (*pEnv
)->SetStaticBooleanField( pEnv
, cls
, hasAutoCField
, hasAutoC
);
197 return kDNSServiceErr_NoError
;
201 static const char* SafeGetUTFChars( JNIEnv
*pEnv
, jstring str
)
202 // Wrapper for JNI GetStringUTFChars() that returns NULL for null str.
204 return str
!= NULL
? (*pEnv
)->GetStringUTFChars( pEnv
, str
, 0) : NULL
;
207 static void SafeReleaseUTFChars( JNIEnv
*pEnv
, jstring str
, const char *buff
)
208 // Wrapper for JNI GetStringUTFChars() that handles null str.
211 (*pEnv
)->ReleaseStringUTFChars( pEnv
, str
, buff
);
216 static void SetupCallbackState( JNIEnv
**ppEnv
)
218 (*gJavaVM
)->AttachCurrentThread( gJavaVM
, (void**) ppEnv
, NULL
);
221 static void TeardownCallbackState( void )
223 (*gJavaVM
)->DetachCurrentThread( gJavaVM
);
226 #else // AUTO_CALLBACKS
228 static void SetupCallbackState( JNIEnv
**ppEnv _UNUSED
)
230 // No setup necessary if ProcessResults() has been called
233 static void TeardownCallbackState( void )
235 // No teardown necessary if ProcessResults() has been called
237 #endif // AUTO_CALLBACKS
240 static OpContext
*NewContext( JNIEnv
*pEnv
, jobject owner
,
241 const char *callbackName
, const char *callbackSig
)
242 // Create and initialize a new OpContext.
244 OpContext
*pContext
= (OpContext
*) malloc( sizeof *pContext
);
246 if ( pContext
!= NULL
)
248 jfieldID clientField
= (*pEnv
)->GetFieldID( pEnv
, (*pEnv
)->GetObjectClass( pEnv
, owner
),
249 "fListener", "Lcom/apple/dnssd/BaseListener;");
251 pContext
->JavaObj
= (*pEnv
)->NewWeakGlobalRef( pEnv
, owner
); // must convert local ref to global to cache;
252 pContext
->ClientObj
= (*pEnv
)->GetObjectField( pEnv
, owner
, clientField
);
253 pContext
->ClientObj
= (*pEnv
)->NewWeakGlobalRef( pEnv
, pContext
->ClientObj
); // must convert local ref to global to cache
254 pContext
->Callback
= (*pEnv
)->GetMethodID( pEnv
,
255 (*pEnv
)->GetObjectClass( pEnv
, pContext
->ClientObj
),
256 callbackName
, callbackSig
);
257 pContext
->Callback2
= NULL
; // not always used
264 static void ReportError( JNIEnv
*pEnv
, jobject target
, jobject service
, DNSServiceErrorType err
)
265 // Invoke operationFailed() method on target with err.
267 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, target
);
268 jmethodID opFailed
= (*pEnv
)->GetMethodID( pEnv
, cls
, "operationFailed",
269 "(Lcom/apple/dnssd/DNSSDService;I)V");
271 (*pEnv
)->CallVoidMethod( pEnv
, target
, opFailed
, service
, err
);
274 JNIEXPORT
void JNICALL
Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv
*pEnv
, jobject pThis
)
275 /* Deallocate the dns_sd service browser and set the Java object's fNativeContext field to 0. */
277 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
278 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
280 if ( contextField
!= 0)
282 OpContext
*pContext
= (OpContext
*) (long) (*pEnv
)->GetLongField(pEnv
, pThis
, contextField
);
283 if ( pContext
!= NULL
)
285 // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate()
286 (*pEnv
)->SetLongField(pEnv
, pThis
, contextField
, 0);
287 if ( pContext
->ServiceRef
!= NULL
)
288 DNSServiceRefDeallocate( pContext
->ServiceRef
);
290 (*pEnv
)->DeleteWeakGlobalRef( pEnv
, pContext
->JavaObj
);
291 (*pEnv
)->DeleteWeakGlobalRef( pEnv
, pContext
->ClientObj
);
298 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv
*pEnv
, jobject pThis
)
299 /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
301 // BlockForData() not supported with AUTO_CALLBACKS
303 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
304 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
306 if ( contextField
!= 0)
308 OpContext
*pContext
= (OpContext
*) (long) (*pEnv
)->GetLongField(pEnv
, pThis
, contextField
);
309 if ( pContext
!= NULL
)
312 int sd
= DNSServiceRefSockFD( pContext
->ServiceRef
);
313 struct timeval timeout
= { 1, 0 };
315 FD_SET( sd
, &readFDs
);
317 // Q: Why do we poll here?
318 // A: Because there's no other thread-safe way to do it.
319 // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not,
320 // and arguably Linux is correct (See <http://www.ussg.iu.edu/hypermail/linux/kernel/0405.1/0418.html>)
321 // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while
322 // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way
323 // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously.
324 // If we try to do this without holding any lock, then right as we jump to the select() routine,
325 // some other thread could stop our operation (thereby closing the socket),
326 // and then that thread (or even some third, unrelated thread)
327 // could do some other DNS-SD operation (or some other operation that opens a new file descriptor)
328 // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor
329 // that may coincidentally have the same numerical value, but is semantically unrelated
330 // to the true file descriptor we thought we were blocking on.
331 // We can't stop this race condition from happening, but at least if we wake up once a second we can detect
332 // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd.
334 if (select( sd
+ 1, &readFDs
, (fd_set
*) NULL
, (fd_set
*) NULL
, &timeout
) == 1) return(1);
337 #endif // !AUTO_CALLBACKS
342 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv
*pEnv
, jobject pThis
)
343 /* Call through to DNSServiceProcessResult() while data remains on socket. */
345 #if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS
347 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
348 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
349 OpContext
*pContext
= (OpContext
*) (long) (*pEnv
)->GetLongField(pEnv
, pThis
, contextField
);
350 DNSServiceErrorType err
= kDNSServiceErr_BadState
;
352 if ( pContext
!= NULL
)
354 int sd
= DNSServiceRefSockFD( pContext
->ServiceRef
);
356 struct timeval zeroTimeout
= { 0, 0 };
358 pContext
->Env
= pEnv
;
361 FD_SET( sd
, &readFDs
);
363 err
= kDNSServiceErr_NoError
;
364 if (0 < select(sd
+ 1, &readFDs
, (fd_set
*) NULL
, (fd_set
*) NULL
, &zeroTimeout
))
366 err
= DNSServiceProcessResult(pContext
->ServiceRef
);
368 // We cannot touch any data structures associated with this operation!
369 // The DNSServiceProcessResult() routine should have invoked our callback,
370 // and our callback could have terminated the operation with op.stop();
371 // and that means HaltOperation() will have been called, which frees pContext.
372 // Basically, from here we just have to get out without touching any stale
373 // data structures that could blow up on us! Particularly, any attempt
374 // to loop here reading more results from the file descriptor is unsafe.
378 #endif // AUTO_CALLBACKS
382 static void DNSSD_API
ServiceBrowseReply( DNSServiceRef sdRef _UNUSED
, DNSServiceFlags flags
, uint32_t interfaceIndex
,
383 DNSServiceErrorType errorCode
, const char *serviceName
, const char *regtype
,
384 const char *replyDomain
, void *context
)
386 OpContext
*pContext
= (OpContext
*) context
;
388 SetupCallbackState( &pContext
->Env
);
390 if ( pContext
->ClientObj
!= NULL
&& pContext
->Callback
!= NULL
)
392 if ( errorCode
== kDNSServiceErr_NoError
)
394 (*pContext
->Env
)->CallVoidMethod( pContext
->Env
, pContext
->ClientObj
,
395 ( flags
& kDNSServiceFlagsAdd
) != 0 ? pContext
->Callback
: pContext
->Callback2
,
396 pContext
->JavaObj
, flags
, interfaceIndex
,
397 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, serviceName
),
398 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, regtype
),
399 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, replyDomain
));
402 ReportError( pContext
->Env
, pContext
->ClientObj
, pContext
->JavaObj
, errorCode
);
405 TeardownCallbackState();
408 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv
*pEnv
, jobject pThis
,
409 jint flags
, jint ifIndex
, jstring regType
, jstring domain
)
411 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
412 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
413 OpContext
*pContext
= NULL
;
414 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
416 if ( contextField
!= 0)
417 pContext
= NewContext( pEnv
, pThis
, "serviceFound",
418 "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
420 err
= kDNSServiceErr_BadParam
;
422 if ( pContext
!= NULL
)
424 const char *regStr
= SafeGetUTFChars( pEnv
, regType
);
425 const char *domainStr
= SafeGetUTFChars( pEnv
, domain
);
427 pContext
->Callback2
= (*pEnv
)->GetMethodID( pEnv
,
428 (*pEnv
)->GetObjectClass( pEnv
, pContext
->ClientObj
),
429 "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
431 err
= DNSServiceBrowse( &pContext
->ServiceRef
, flags
, ifIndex
, regStr
, domainStr
, ServiceBrowseReply
, pContext
);
432 if ( err
== kDNSServiceErr_NoError
)
434 (*pEnv
)->SetLongField(pEnv
, pThis
, contextField
, (long) pContext
);
437 SafeReleaseUTFChars( pEnv
, regType
, regStr
);
438 SafeReleaseUTFChars( pEnv
, domain
, domainStr
);
441 err
= kDNSServiceErr_NoMemory
;
447 static void DNSSD_API
ServiceResolveReply( DNSServiceRef sdRef _UNUSED
, DNSServiceFlags flags
, uint32_t interfaceIndex
,
448 DNSServiceErrorType errorCode
, const char *fullname
, const char *hosttarget
,
449 uint16_t port
, uint16_t txtLen
, const unsigned char *txtRecord
, void *context
)
451 OpContext
*pContext
= (OpContext
*) context
;
458 SetupCallbackState( &pContext
->Env
);
460 txtCls
= (*pContext
->Env
)->FindClass( pContext
->Env
, "com/apple/dnssd/TXTRecord");
461 txtCtor
= (*pContext
->Env
)->GetMethodID( pContext
->Env
, txtCls
, "<init>", "([B)V");
463 if ( pContext
->ClientObj
!= NULL
&& pContext
->Callback
!= NULL
&& txtCtor
!= NULL
&&
464 NULL
!= ( txtBytes
= (*pContext
->Env
)->NewByteArray( pContext
->Env
, txtLen
)))
466 if ( errorCode
== kDNSServiceErr_NoError
)
468 // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit
469 // pattern into a number here.
470 port
= ( ((unsigned char*) &port
)[0] << 8) | ((unsigned char*) &port
)[1];
472 // Initialize txtBytes with contents of txtRecord
473 pBytes
= (*pContext
->Env
)->GetByteArrayElements( pContext
->Env
, txtBytes
, NULL
);
474 memcpy( pBytes
, txtRecord
, txtLen
);
475 (*pContext
->Env
)->ReleaseByteArrayElements( pContext
->Env
, txtBytes
, pBytes
, JNI_COMMIT
);
477 // Construct txtObj with txtBytes
478 txtObj
= (*pContext
->Env
)->NewObject( pContext
->Env
, txtCls
, txtCtor
, txtBytes
);
479 (*pContext
->Env
)->DeleteLocalRef( pContext
->Env
, txtBytes
);
481 (*pContext
->Env
)->CallVoidMethod( pContext
->Env
, pContext
->ClientObj
, pContext
->Callback
,
482 pContext
->JavaObj
, flags
, interfaceIndex
,
483 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, fullname
),
484 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, hosttarget
),
488 ReportError( pContext
->Env
, pContext
->ClientObj
, pContext
->JavaObj
, errorCode
);
491 TeardownCallbackState();
494 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv
*pEnv
, jobject pThis
,
495 jint flags
, jint ifIndex
, jstring serviceName
, jstring regType
, jstring domain
)
497 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
498 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
499 OpContext
*pContext
= NULL
;
500 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
502 if ( contextField
!= 0)
503 pContext
= NewContext( pEnv
, pThis
, "serviceResolved",
504 "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V");
506 err
= kDNSServiceErr_BadParam
;
508 if ( pContext
!= NULL
)
510 const char *servStr
= SafeGetUTFChars( pEnv
, serviceName
);
511 const char *regStr
= SafeGetUTFChars( pEnv
, regType
);
512 const char *domainStr
= SafeGetUTFChars( pEnv
, domain
);
514 err
= DNSServiceResolve( &pContext
->ServiceRef
, flags
, ifIndex
,
515 servStr
, regStr
, domainStr
, ServiceResolveReply
, pContext
);
516 if ( err
== kDNSServiceErr_NoError
)
518 (*pEnv
)->SetLongField(pEnv
, pThis
, contextField
, (long) pContext
);
521 SafeReleaseUTFChars( pEnv
, serviceName
, servStr
);
522 SafeReleaseUTFChars( pEnv
, regType
, regStr
);
523 SafeReleaseUTFChars( pEnv
, domain
, domainStr
);
526 err
= kDNSServiceErr_NoMemory
;
532 static void DNSSD_API
ServiceRegisterReply( DNSServiceRef sdRef _UNUSED
, DNSServiceFlags flags
,
533 DNSServiceErrorType errorCode
, const char *serviceName
,
534 const char *regType
, const char *domain
, void *context
)
536 OpContext
*pContext
= (OpContext
*) context
;
538 SetupCallbackState( &pContext
->Env
);
540 if ( pContext
->ClientObj
!= NULL
&& pContext
->Callback
!= NULL
)
542 if ( errorCode
== kDNSServiceErr_NoError
)
544 (*pContext
->Env
)->CallVoidMethod( pContext
->Env
, pContext
->ClientObj
, pContext
->Callback
,
545 pContext
->JavaObj
, flags
,
546 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, serviceName
),
547 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, regType
),
548 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, domain
));
551 ReportError( pContext
->Env
, pContext
->ClientObj
, pContext
->JavaObj
, errorCode
);
553 TeardownCallbackState();
556 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleRegistration_BeginRegister( JNIEnv
*pEnv
, jobject pThis
,
557 jint ifIndex
, jint flags
, jstring serviceName
, jstring regType
,
558 jstring domain
, jstring host
, jint port
, jbyteArray txtRecord
)
560 //syslog(LOG_ERR, "BR");
561 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
562 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
563 OpContext
*pContext
= NULL
;
564 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
568 //syslog(LOG_ERR, "BR: contextField %d", contextField);
570 if ( contextField
!= 0)
571 pContext
= NewContext( pEnv
, pThis
, "serviceRegistered",
572 "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
574 err
= kDNSServiceErr_BadParam
;
576 if ( pContext
!= NULL
)
578 const char *servStr
= SafeGetUTFChars( pEnv
, serviceName
);
579 const char *regStr
= SafeGetUTFChars( pEnv
, regType
);
580 const char *domainStr
= SafeGetUTFChars( pEnv
, domain
);
581 const char *hostStr
= SafeGetUTFChars( pEnv
, host
);
583 //syslog(LOG_ERR, "BR: regStr %s", regStr);
585 // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a
586 // big-endian number into a 16-bit pattern here.
587 uint16_t portBits
= port
;
588 portBits
= ( ((unsigned char*) &portBits
)[0] << 8) | ((unsigned char*) &portBits
)[1];
590 pBytes
= txtRecord
? (*pEnv
)->GetByteArrayElements( pEnv
, txtRecord
, NULL
) : NULL
;
591 numBytes
= txtRecord
? (*pEnv
)->GetArrayLength( pEnv
, txtRecord
) : 0;
593 err
= DNSServiceRegister( &pContext
->ServiceRef
, flags
, ifIndex
, servStr
, regStr
,
594 domainStr
, hostStr
, portBits
,
595 numBytes
, pBytes
, ServiceRegisterReply
, pContext
);
596 if ( err
== kDNSServiceErr_NoError
)
598 (*pEnv
)->SetLongField(pEnv
, pThis
, contextField
, (long) pContext
);
602 (*pEnv
)->ReleaseByteArrayElements( pEnv
, txtRecord
, pBytes
, 0);
604 SafeReleaseUTFChars( pEnv
, serviceName
, servStr
);
605 SafeReleaseUTFChars( pEnv
, regType
, regStr
);
606 SafeReleaseUTFChars( pEnv
, domain
, domainStr
);
607 SafeReleaseUTFChars( pEnv
, host
, hostStr
);
610 err
= kDNSServiceErr_NoMemory
;
615 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv
*pEnv
, jobject pThis
,
616 jint flags
, jint rrType
, jbyteArray rData
, jint ttl
, jobject destObj
)
618 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
619 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
620 jclass destCls
= (*pEnv
)->GetObjectClass( pEnv
, destObj
);
621 jfieldID recField
= (*pEnv
)->GetFieldID( pEnv
, destCls
, "fRecord", "J");
622 OpContext
*pContext
= NULL
;
623 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
628 if ( contextField
!= 0)
629 pContext
= (OpContext
*) (long) (*pEnv
)->GetLongField(pEnv
, pThis
, contextField
);
630 if ( pContext
== NULL
|| pContext
->ServiceRef
== NULL
)
631 return kDNSServiceErr_BadParam
;
633 pBytes
= (*pEnv
)->GetByteArrayElements( pEnv
, rData
, NULL
);
634 numBytes
= (*pEnv
)->GetArrayLength( pEnv
, rData
);
636 err
= DNSServiceAddRecord( pContext
->ServiceRef
, &recRef
, flags
, rrType
, numBytes
, pBytes
, ttl
);
637 if ( err
== kDNSServiceErr_NoError
)
639 (*pEnv
)->SetLongField(pEnv
, destObj
, recField
, (long) recRef
);
643 (*pEnv
)->ReleaseByteArrayElements( pEnv
, rData
, pBytes
, 0);
648 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv
*pEnv
, jobject pThis
,
649 jint flags
, jbyteArray rData
, jint ttl
)
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", "J");
654 OpContext
*pContext
= NULL
;
655 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
658 DNSRecordRef recRef
= NULL
;
660 if ( ownerField
!= 0)
662 jobject ownerObj
= (*pEnv
)->GetObjectField( pEnv
, pThis
, ownerField
);
663 jclass ownerClass
= (*pEnv
)->GetObjectClass( pEnv
, ownerObj
);
664 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, ownerClass
, "fNativeContext", "J");
665 if ( contextField
!= 0)
666 pContext
= (OpContext
*) (long) (*pEnv
)->GetLongField(pEnv
, ownerObj
, contextField
);
669 recRef
= (DNSRecordRef
) (long) (*pEnv
)->GetLongField(pEnv
, pThis
, recField
);
670 if ( pContext
== NULL
|| pContext
->ServiceRef
== NULL
)
671 return kDNSServiceErr_BadParam
;
673 pBytes
= (*pEnv
)->GetByteArrayElements( pEnv
, rData
, NULL
);
674 numBytes
= (*pEnv
)->GetArrayLength( pEnv
, rData
);
676 err
= DNSServiceUpdateRecord( pContext
->ServiceRef
, recRef
, flags
, numBytes
, pBytes
, ttl
);
679 (*pEnv
)->ReleaseByteArrayElements( pEnv
, rData
, pBytes
, 0);
684 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv
*pEnv
, jobject pThis
)
686 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
687 jfieldID ownerField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fOwner", "Lcom/apple/dnssd/AppleService;");
688 jfieldID recField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fRecord", "J");
689 OpContext
*pContext
= NULL
;
690 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
691 DNSRecordRef recRef
= NULL
;
693 if ( ownerField
!= 0)
695 jobject ownerObj
= (*pEnv
)->GetObjectField( pEnv
, pThis
, ownerField
);
696 jclass ownerClass
= (*pEnv
)->GetObjectClass( pEnv
, ownerObj
);
697 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, ownerClass
, "fNativeContext", "J");
698 if ( contextField
!= 0)
699 pContext
= (OpContext
*) (long) (*pEnv
)->GetLongField(pEnv
, ownerObj
, contextField
);
702 recRef
= (DNSRecordRef
) (long) (*pEnv
)->GetLongField(pEnv
, pThis
, recField
);
703 if ( pContext
== NULL
|| pContext
->ServiceRef
== NULL
)
704 return kDNSServiceErr_BadParam
;
706 err
= DNSServiceRemoveRecord( pContext
->ServiceRef
, recRef
, 0);
712 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection( JNIEnv
*pEnv
, jobject pThis
)
714 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
715 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
716 OpContext
*pContext
= NULL
;
717 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
719 if ( contextField
!= 0)
720 pContext
= NewContext( pEnv
, pThis
, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V");
722 err
= kDNSServiceErr_BadParam
;
724 if ( pContext
!= NULL
)
726 err
= DNSServiceCreateConnection( &pContext
->ServiceRef
);
727 if ( err
== kDNSServiceErr_NoError
)
729 (*pEnv
)->SetLongField(pEnv
, pThis
, contextField
, (long) pContext
);
733 err
= kDNSServiceErr_NoMemory
;
738 struct RecordRegistrationRef
743 typedef struct RecordRegistrationRef RecordRegistrationRef
;
745 static void DNSSD_API
RegisterRecordReply( DNSServiceRef sdRef _UNUSED
,
746 DNSRecordRef recordRef _UNUSED
, DNSServiceFlags flags
,
747 DNSServiceErrorType errorCode
, void *context
)
749 RecordRegistrationRef
*regEnvelope
= (RecordRegistrationRef
*) context
;
750 OpContext
*pContext
= regEnvelope
->Context
;
752 SetupCallbackState( &pContext
->Env
);
754 if ( pContext
->ClientObj
!= NULL
&& pContext
->Callback
!= NULL
)
756 if ( errorCode
== kDNSServiceErr_NoError
)
758 (*pContext
->Env
)->CallVoidMethod( pContext
->Env
, pContext
->ClientObj
, pContext
->Callback
,
759 regEnvelope
->RecordObj
, flags
);
762 ReportError( pContext
->Env
, pContext
->ClientObj
, pContext
->JavaObj
, errorCode
);
765 (*pContext
->Env
)->DeleteWeakGlobalRef( pContext
->Env
, regEnvelope
->RecordObj
);
768 TeardownCallbackState();
771 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv
*pEnv
, jobject pThis
,
772 jint flags
, jint ifIndex
, jstring fullname
, jint rrType
, jint rrClass
,
773 jbyteArray rData
, jint ttl
, jobject destObj
)
775 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
776 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
777 jclass destCls
= (*pEnv
)->GetObjectClass( pEnv
, destObj
);
778 jfieldID recField
= (*pEnv
)->GetFieldID( pEnv
, destCls
, "fRecord", "J");
779 const char *nameStr
= SafeGetUTFChars( pEnv
, fullname
);
780 OpContext
*pContext
= NULL
;
781 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
785 RecordRegistrationRef
*regEnvelope
;
787 if ( contextField
!= 0)
788 pContext
= (OpContext
*) (long) (*pEnv
)->GetLongField(pEnv
, pThis
, contextField
);
789 if ( pContext
== NULL
|| pContext
->ServiceRef
== NULL
|| nameStr
== NULL
)
790 return kDNSServiceErr_BadParam
;
792 regEnvelope
= calloc( 1, sizeof *regEnvelope
);
793 if ( regEnvelope
== NULL
)
794 return kDNSServiceErr_NoMemory
;
795 regEnvelope
->Context
= pContext
;
796 regEnvelope
->RecordObj
= (*pEnv
)->NewWeakGlobalRef( pEnv
, destObj
); // must convert local ref to global to cache
798 pBytes
= (*pEnv
)->GetByteArrayElements( pEnv
, rData
, NULL
);
799 numBytes
= (*pEnv
)->GetArrayLength( pEnv
, rData
);
801 err
= DNSServiceRegisterRecord( pContext
->ServiceRef
, &recRef
, flags
, ifIndex
,
802 nameStr
, rrType
, rrClass
, numBytes
, pBytes
, ttl
,
803 RegisterRecordReply
, regEnvelope
);
805 if ( err
== kDNSServiceErr_NoError
)
807 (*pEnv
)->SetLongField(pEnv
, destObj
, recField
, (long) recRef
);
811 if ( regEnvelope
->RecordObj
!= NULL
)
812 (*pEnv
)->DeleteWeakGlobalRef( pEnv
, regEnvelope
->RecordObj
);
817 (*pEnv
)->ReleaseByteArrayElements( pEnv
, rData
, pBytes
, 0);
819 SafeReleaseUTFChars( pEnv
, fullname
, nameStr
);
825 static void DNSSD_API
ServiceQueryReply( DNSServiceRef sdRef _UNUSED
, DNSServiceFlags flags
, uint32_t interfaceIndex
,
826 DNSServiceErrorType errorCode
, const char *serviceName
,
827 uint16_t rrtype
, uint16_t rrclass
, uint16_t rdlen
,
828 const void *rdata
, uint32_t ttl
, void *context
)
830 OpContext
*pContext
= (OpContext
*) context
;
834 SetupCallbackState( &pContext
->Env
);
836 if ( pContext
->ClientObj
!= NULL
&& pContext
->Callback
!= NULL
&&
837 NULL
!= ( rDataObj
= (*pContext
->Env
)->NewByteArray( pContext
->Env
, rdlen
)))
839 if ( errorCode
== kDNSServiceErr_NoError
)
841 // Initialize rDataObj with contents of rdata
842 pBytes
= (*pContext
->Env
)->GetByteArrayElements( pContext
->Env
, rDataObj
, NULL
);
843 memcpy( pBytes
, rdata
, rdlen
);
844 (*pContext
->Env
)->ReleaseByteArrayElements( pContext
->Env
, rDataObj
, pBytes
, JNI_COMMIT
);
846 (*pContext
->Env
)->CallVoidMethod( pContext
->Env
, pContext
->ClientObj
, pContext
->Callback
,
847 pContext
->JavaObj
, flags
, interfaceIndex
,
848 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, serviceName
),
849 rrtype
, rrclass
, rDataObj
, ttl
);
852 ReportError( pContext
->Env
, pContext
->ClientObj
, pContext
->JavaObj
, errorCode
);
854 TeardownCallbackState();
857 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv
*pEnv
, jobject pThis
,
858 jint flags
, jint ifIndex
, jstring serviceName
, jint rrtype
, jint rrclass
)
860 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
861 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
862 OpContext
*pContext
= NULL
;
863 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
865 if ( contextField
!= 0)
866 pContext
= NewContext( pEnv
, pThis
, "queryAnswered",
867 "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V");
869 err
= kDNSServiceErr_BadParam
;
871 if ( pContext
!= NULL
)
873 const char *servStr
= SafeGetUTFChars( pEnv
, serviceName
);
875 err
= DNSServiceQueryRecord( &pContext
->ServiceRef
, flags
, ifIndex
, servStr
,
876 rrtype
, rrclass
, ServiceQueryReply
, pContext
);
877 if ( err
== kDNSServiceErr_NoError
)
879 (*pEnv
)->SetLongField(pEnv
, pThis
, contextField
, (long) pContext
);
882 SafeReleaseUTFChars( pEnv
, serviceName
, servStr
);
885 err
= kDNSServiceErr_NoMemory
;
891 static void DNSSD_API
DomainEnumReply( DNSServiceRef sdRef _UNUSED
, DNSServiceFlags flags
, uint32_t interfaceIndex
,
892 DNSServiceErrorType errorCode
, const char *replyDomain
, void *context
)
894 OpContext
*pContext
= (OpContext
*) context
;
896 SetupCallbackState( &pContext
->Env
);
898 if ( pContext
->ClientObj
!= NULL
&& pContext
->Callback
!= NULL
)
900 if ( errorCode
== kDNSServiceErr_NoError
)
902 (*pContext
->Env
)->CallVoidMethod( pContext
->Env
, pContext
->ClientObj
,
903 ( flags
& kDNSServiceFlagsAdd
) != 0 ? pContext
->Callback
: pContext
->Callback2
,
904 pContext
->JavaObj
, flags
, interfaceIndex
,
905 (*pContext
->Env
)->NewStringUTF( pContext
->Env
, replyDomain
));
908 ReportError( pContext
->Env
, pContext
->ClientObj
, pContext
->JavaObj
, errorCode
);
910 TeardownCallbackState();
913 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleDomainEnum_BeginEnum( JNIEnv
*pEnv
, jobject pThis
,
914 jint flags
, jint ifIndex
)
916 jclass cls
= (*pEnv
)->GetObjectClass( pEnv
, pThis
);
917 jfieldID contextField
= (*pEnv
)->GetFieldID( pEnv
, cls
, "fNativeContext", "J");
918 OpContext
*pContext
= NULL
;
919 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
921 if ( contextField
!= 0)
922 pContext
= NewContext( pEnv
, pThis
, "domainFound",
923 "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V");
925 err
= kDNSServiceErr_BadParam
;
927 if ( pContext
!= NULL
)
929 pContext
->Callback2
= (*pEnv
)->GetMethodID( pEnv
,
930 (*pEnv
)->GetObjectClass( pEnv
, pContext
->ClientObj
),
931 "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V");
933 err
= DNSServiceEnumerateDomains( &pContext
->ServiceRef
, flags
, ifIndex
,
934 DomainEnumReply
, pContext
);
935 if ( err
== kDNSServiceErr_NoError
)
937 (*pEnv
)->SetLongField(pEnv
, pThis
, contextField
, (long) pContext
);
941 err
= kDNSServiceErr_NoMemory
;
947 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleDNSSD_ConstructName( JNIEnv
*pEnv
, jobject pThis _UNUSED
,
948 jstring serviceName
, jstring regtype
, jstring domain
, jobjectArray pOut
)
950 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
951 const char *nameStr
= SafeGetUTFChars( pEnv
, serviceName
);
952 const char *regStr
= SafeGetUTFChars( pEnv
, regtype
);
953 const char *domStr
= SafeGetUTFChars( pEnv
, domain
);
954 char buff
[ kDNSServiceMaxDomainName
+ 1];
956 err
= DNSServiceConstructFullName( buff
, nameStr
, regStr
, domStr
);
958 if ( err
== kDNSServiceErr_NoError
)
960 // pOut is expected to be a String[1] array.
961 (*pEnv
)->SetObjectArrayElement( pEnv
, pOut
, 0, (*pEnv
)->NewStringUTF( pEnv
, buff
));
964 SafeReleaseUTFChars( pEnv
, serviceName
, nameStr
);
965 SafeReleaseUTFChars( pEnv
, regtype
, regStr
);
966 SafeReleaseUTFChars( pEnv
, domain
, domStr
);
971 JNIEXPORT
void JNICALL
Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv
*pEnv
, jobject pThis _UNUSED
,
972 jint flags
, jint ifIndex
, jstring fullName
,
973 jint rrtype
, jint rrclass
, jbyteArray rdata
)
977 const char *nameStr
= SafeGetUTFChars( pEnv
, fullName
);
979 pBytes
= (*pEnv
)->GetByteArrayElements( pEnv
, rdata
, NULL
);
980 numBytes
= (*pEnv
)->GetArrayLength( pEnv
, rdata
);
982 DNSServiceReconfirmRecord( flags
, ifIndex
, nameStr
, rrtype
, rrclass
, numBytes
, pBytes
);
985 (*pEnv
)->ReleaseByteArrayElements( pEnv
, rdata
, pBytes
, 0);
987 SafeReleaseUTFChars( pEnv
, fullName
, nameStr
);
990 #define LOCAL_ONLY_NAME "loo"
992 JNIEXPORT jstring JNICALL
Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv
*pEnv
, jobject pThis _UNUSED
,
995 char *p
= LOCAL_ONLY_NAME
, nameBuff
[IF_NAMESIZE
];
997 if (ifIndex
!= (jint
) kDNSServiceInterfaceIndexLocalOnly
)
998 p
= if_indextoname( ifIndex
, nameBuff
);
1000 return (*pEnv
)->NewStringUTF( pEnv
, p
);
1004 JNIEXPORT jint JNICALL
Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv
*pEnv
, jobject pThis _UNUSED
,
1007 uint32_t ifIndex
= kDNSServiceInterfaceIndexLocalOnly
;
1008 const char *nameStr
= SafeGetUTFChars( pEnv
, ifName
);
1010 if (strcmp(nameStr
, LOCAL_ONLY_NAME
))
1011 ifIndex
= if_nametoindex( nameStr
);
1013 SafeReleaseUTFChars( pEnv
, ifName
, nameStr
);
1021 if_indextoname( DWORD ifIndex
, char * nameBuff
)
1023 PIP_ADAPTER_INFO pAdapterInfo
= NULL
;
1024 PIP_ADAPTER_INFO pAdapter
= NULL
;
1026 char * ifName
= NULL
;
1027 ULONG ulOutBufLen
= 0;
1029 if (GetAdaptersInfo( NULL
, &ulOutBufLen
) != ERROR_BUFFER_OVERFLOW
)
1034 pAdapterInfo
= (IP_ADAPTER_INFO
*) malloc(ulOutBufLen
);
1036 if (pAdapterInfo
== NULL
)
1041 dwRetVal
= GetAdaptersInfo( pAdapterInfo
, &ulOutBufLen
);
1043 if (dwRetVal
!= NO_ERROR
)
1048 pAdapter
= pAdapterInfo
;
1051 if (pAdapter
->Index
== ifIndex
)
1053 // It would be better if we passed in the length of nameBuff to this
1054 // function, so we would have absolute certainty that no buffer
1055 // overflows would occur. Buffer overflows *shouldn't* occur because
1056 // nameBuff is of size MAX_ADAPTER_NAME_LENGTH.
1057 strcpy( nameBuff
, pAdapter
->AdapterName
);
1062 pAdapter
= pAdapter
->Next
;
1067 if (pAdapterInfo
!= NULL
)
1069 free( pAdapterInfo
);
1070 pAdapterInfo
= NULL
;
1078 if_nametoindex( const char * nameStr
)
1080 PIP_ADAPTER_INFO pAdapterInfo
= NULL
;
1081 PIP_ADAPTER_INFO pAdapter
= NULL
;
1084 ULONG ulOutBufLen
= 0;
1086 if (GetAdaptersInfo( NULL
, &ulOutBufLen
) != ERROR_BUFFER_OVERFLOW
)
1091 pAdapterInfo
= (IP_ADAPTER_INFO
*) malloc(ulOutBufLen
);
1093 if (pAdapterInfo
== NULL
)
1098 dwRetVal
= GetAdaptersInfo( pAdapterInfo
, &ulOutBufLen
);
1100 if (dwRetVal
!= NO_ERROR
)
1105 pAdapter
= pAdapterInfo
;
1108 if (strcmp(pAdapter
->AdapterName
, nameStr
) == 0)
1110 ifIndex
= pAdapter
->Index
;
1114 pAdapter
= pAdapter
->Next
;
1119 if (pAdapterInfo
!= NULL
)
1121 free( pAdapterInfo
);
1122 pAdapterInfo
= NULL
;
1130 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
1131 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
1132 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
1133 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
1134 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
1136 // NOT static -- otherwise the compiler may optimize it out
1137 // The "@(#) " pattern is a special prefix the "what" command looks for
1138 const char VersionString_SCCS
[] = "@(#) libjdns_sd " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";