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