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>
49static char *   win32_if_indextoname( DWORD ifIndex, char * nameBuff);
50static 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
76enum {
77    kInterfaceVersionOne = 1,
78    kInterfaceVersionCurrent        // Must match version in .jar file
79};
80
81typedef struct OpContext OpContext;
82
83struct  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
95JavaVM      *gJavaVM = NULL;
96#endif
97
98
99JNIEXPORT 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
130static 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
136static 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
145static void SetupCallbackState( JNIEnv **ppEnv)
146{
147    (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL);
148}
149
150static void TeardownCallbackState( void )
151{
152    (*gJavaVM)->DetachCurrentThread( gJavaVM);
153}
154
155#else   // AUTO_CALLBACKS
156
157static void SetupCallbackState( JNIEnv **ppEnv _UNUSED)
158{
159    // No setup necessary if ProcessResults() has been called
160}
161
162static void TeardownCallbackState( void )
163{
164    // No teardown necessary if ProcessResults() has been called
165}
166#endif  // AUTO_CALLBACKS
167
168
169static 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
193static 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
203JNIEXPORT 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
227JNIEXPORT 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
271JNIEXPORT 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
311static 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
337JNIEXPORT 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
376static 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
423JNIEXPORT 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
461static 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
485JNIEXPORT 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
544JNIEXPORT 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
577JNIEXPORT 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
613JNIEXPORT 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
641JNIEXPORT 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
667struct RecordRegistrationRef
668{
669    OpContext       *Context;
670    jobject RecordObj;
671};
672typedef struct RecordRegistrationRef RecordRegistrationRef;
673
674static 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
700JNIEXPORT 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
754static 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
786JNIEXPORT 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
820static 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
842JNIEXPORT 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
876JNIEXPORT 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
900JNIEXPORT 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
922JNIEXPORT 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
936JNIEXPORT 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)
954static char*
955win32_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
999exit:
1000
1001    if (pAdapterInfo != NULL)
1002    {
1003        free( pAdapterInfo );
1004        pAdapterInfo = NULL;
1005    }
1006
1007    return ifName;
1008}
1009
1010
1011static DWORD
1012win32_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
1051exit:
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
1072const char VersionString_SCCS[] = "@(#) libjdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
1073