/* -*- Mode: C; tab-width: 4 -*-
 *
 * Copyright (c) 2007-2018 Apple Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/vm_map.h>
#include <servers/bootstrap.h>
#include <IOKit/IOReturn.h>
#include <CoreFoundation/CoreFoundation.h>
#include "mDNSDebug.h"
#include "helper.h"
#include <dispatch/dispatch.h>
#include <arpa/inet.h>
#include <xpc/private.h>
#include <Block.h>

//
// Implementation Notes about the HelperQueue:
//
// To prevent blocking the main queue, all communications with mDNSResponderHelper should happen on
// HelperQueue. There are a few calls which are still synchronous and needs to be handled separately
// case by case.
//
// When spawning off the work to the HelperQueue, any arguments that are pointers need to be copied
// explicitly as they may cease to exist after the call returns. From within the block that is scheduled,
// arrays defined on the stack can't be referenced and hence it is enclosed them in a struct. If the array is
// an argument to the function, the blocks can reference them as they are passed in as pointers. But care should
// be taken to copy them locally as they may cease to exist when the function returns.
//


//*************************************************************************************************************
// Globals
static dispatch_queue_t HelperQueue;
static xpc_connection_t helper_xpc_conn = NULL;

static int64_t maxwait_secs = 5LL;

#define mDNSHELPER_DEBUG LogOperation

//*************************************************************************************************************
// Utility Functions

static void HelperLog(const char *prefix, xpc_object_t o)
{
    char *desc = xpc_copy_description(o);
    mDNSHELPER_DEBUG("HelperLog %s: %s", prefix, desc);
    free(desc);
}

//*************************************************************************************************************
// XPC Funcs:
//*************************************************************************************************************


mDNSlocal void Init_Connection(const char *servname)
{
    helper_xpc_conn = xpc_connection_create_mach_service(servname, HelperQueue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
    
    xpc_connection_set_event_handler(helper_xpc_conn, ^(xpc_object_t event)
    {
        mDNSHELPER_DEBUG("Init_Connection xpc: [%s] \n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
    });
    
    xpc_connection_resume(helper_xpc_conn);
}

mDNSlocal int SendDict_ToServer(xpc_object_t msg)
{
    __block int errorcode = kHelperErr_NoResponse;
    
    HelperLog("SendDict_ToServer Sending msg to Daemon", msg);
    
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    dispatch_retain(sem); // for the block below
    
    xpc_connection_send_message_with_reply(helper_xpc_conn, msg, HelperQueue, ^(xpc_object_t recv_msg)
    {
        xpc_type_t type = xpc_get_type(recv_msg);
                                               
        if (type == XPC_TYPE_DICTIONARY)
        {
            HelperLog("SendDict_ToServer Received reply msg from Daemon", recv_msg);
            uint64_t reply_status = xpc_dictionary_get_uint64(recv_msg, kHelperReplyStatus);
            errorcode = xpc_dictionary_get_int64(recv_msg, kHelperErrCode);
            
            switch (reply_status)
            {
                case kHelperReply_ACK:
                    mDNSHELPER_DEBUG("NoError: successful reply");
                    break;
                default:
                    LogMsg("default: Unexpected reply from Helper");
                    break;
            }
        }
        else
        {
            LogMsg("SendDict_ToServer Received unexpected reply from daemon [%s]",
                    xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION));
            HelperLog("SendDict_ToServer Unexpected Reply contents", recv_msg);
        }
        
        dispatch_semaphore_signal(sem);
        dispatch_release(sem);
        
    });
    
    if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (maxwait_secs * NSEC_PER_SEC))) != 0)
        LogMsg("SendDict_ToServer: UNEXPECTED WAIT_TIME in dispatch_semaphore_wait");
    
    dispatch_release(sem);
    
    mDNSHELPER_DEBUG("SendDict_ToServer returning with errorcode[%d]", errorcode);
    
    return errorcode;
}

mDNSlocal xpc_object_t SendDict_GetReply(xpc_object_t msg)
{
    // Create empty dictionary
    __block xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    if (!dict) return NULL;
    xpc_retain(dict);

    HelperLog("SendDict_GetReply Sending msg to Daemon", msg);
    
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    dispatch_retain(sem); // for the block below
    
    xpc_connection_send_message_with_reply(helper_xpc_conn, msg, HelperQueue, ^(xpc_object_t recv_msg)
    {
        xpc_type_t type = xpc_get_type(recv_msg);
                                               
        if (type == XPC_TYPE_DICTIONARY)
        {
            HelperLog("SendDict_GetReply Received reply msg from Daemon", recv_msg);
            uint64_t reply_status = xpc_dictionary_get_uint64(recv_msg, kHelperReplyStatus);
            
            switch (reply_status)
            {
                case kHelperReply_ACK:
                    mDNSHELPER_DEBUG("NoError: successful reply");
                    break;
                default:
                    LogMsg("default: Unexpected reply from Helper");
                    break;
            }
            // Copy result into dict reply
            xpc_dictionary_apply(recv_msg, ^bool(const char *key, xpc_object_t value)
            {
                xpc_dictionary_set_value(dict, key, value);
                return true;
            });
        }
        else
        {
            LogMsg("SendDict_GetReply Received unexpected reply from daemon [%s]",
                    xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION));
            HelperLog("SendDict_GetReply Unexpected Reply contents", recv_msg);
        }
        
        dispatch_semaphore_signal(sem);
        dispatch_release(sem);
        xpc_release(dict);

    });
    
    if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (maxwait_secs * NSEC_PER_SEC))) != 0)
    {
        LogMsg("SendDict_GetReply: UNEXPECTED WAIT_TIME in dispatch_semaphore_wait");
        xpc_release(dict);
        dispatch_release(sem);

        return NULL;
    }
    
    dispatch_release(sem);
    
    return dict;
}

//**************************************************************************************************************

mDNSexport mStatus mDNSHelperInit()
{
    HelperQueue = dispatch_queue_create("com.apple.mDNSResponder.HelperQueue", NULL);
    if (HelperQueue == NULL)
    {
        LogMsg("dispatch_queue_create: Helper queue NULL");
        return mStatus_NoMemoryErr;
    }
    return mStatus_NoError;
}

void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new)
{
    struct
    {
        char oldname[MAX_DOMAIN_LABEL+1];
        char newname[MAX_DOMAIN_LABEL+1];
    } names;

    mDNSPlatformMemZero(names.oldname, MAX_DOMAIN_LABEL + 1);
    mDNSPlatformMemZero(names.newname, MAX_DOMAIN_LABEL + 1);

    ConvertDomainLabelToCString_unescaped(old, names.oldname);
    
    if (new)
        ConvertDomainLabelToCString_unescaped(new, names.newname);
    
    
    mDNSHELPER_DEBUG("mDNSPreferencesSetName: XPC IPC Test oldname %s newname %s", names.oldname, names.newname);
    Init_Connection(kHelperService);
     
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, set_name);
    
    xpc_dictionary_set_uint64(dict, kPrefsNameKey, key);
    xpc_dictionary_set_string(dict, kPrefsOldName, names.oldname);
    xpc_dictionary_set_string(dict, kPrefsNewName, names.newname);
    
    SendDict_ToServer(dict);
    xpc_release(dict);
    dict = NULL;
    
}

void mDNSRequestBPF()
{
     mDNSHELPER_DEBUG("mDNSRequestBPF: Using XPC IPC");
     Init_Connection(kHelperService);
     
     // Create Dictionary To Send
     xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
     xpc_dictionary_set_uint64(dict, kHelperMode, bpf_request);
     SendDict_ToServer(dict);
     xpc_release(dict);
     dict = NULL;

}

int mDNSPowerRequest(int key, int interval)
{
    int err_code = kHelperErr_NotConnected;
    
    mDNSHELPER_DEBUG("mDNSPowerRequest: Using XPC IPC calling out to Helper key is [%d] interval is [%d]", key, interval);
    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, power_req);
    xpc_dictionary_set_uint64(dict, "powerreq_key", key);
    xpc_dictionary_set_uint64(dict, "powerreq_interval", interval);
    
    err_code = SendDict_ToServer(dict);
    xpc_release(dict);
    dict = NULL;
    
    mDNSHELPER_DEBUG("mDNSPowerRequest: Using XPC IPC returning error_code %d", err_code);
    return err_code;
}

int mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth)
{
    int err_code = kHelperErr_NotConnected;
    
    mDNSHELPER_DEBUG("mDNSSetLocalAddressCacheEntry: Using XPC IPC calling out to Helper: ifindex is [%d] family is [%d]", ifindex, family);
    
    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, set_localaddr_cacheentry);
    
    xpc_dictionary_set_uint64(dict, "slace_ifindex", ifindex);
    xpc_dictionary_set_uint64(dict, "slace_family", family);
    
    xpc_dictionary_set_data(dict, "slace_ip", (uint8_t*)ip, sizeof(v6addr_t));
    xpc_dictionary_set_data(dict, "slace_eth", (uint8_t*)eth, sizeof(ethaddr_t));
    
    err_code = SendDict_ToServer(dict);
    xpc_release(dict);
    dict = NULL;
    
    mDNSHELPER_DEBUG("mDNSSetLocalAddressCacheEntry: Using XPC IPC returning error_code %d", err_code);
    return err_code;
}


void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text
{
    mDNSHELPER_DEBUG("mDNSNotify() calling out to Helper XPC IPC title[%s] msg[%s]", title, msg);

    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, user_notify);
    
    xpc_dictionary_set_string(dict, "notify_title", title);
    xpc_dictionary_set_string(dict, "notify_msg", msg);
    
    SendDict_ToServer(dict);
    xpc_release(dict);
    dict = NULL;
    
}


int mDNSKeychainGetSecrets(CFArrayRef *result)
{
    
    CFPropertyListRef plist = NULL;
    CFDataRef bytes = NULL;
    unsigned int numsecrets = 0;
    unsigned int secretsCnt = 0;
    int error_code = kHelperErr_NotConnected;
    xpc_object_t reply_dict = NULL;
    const void *sec = NULL;
    size_t secrets_size = 0;
    
    mDNSHELPER_DEBUG("mDNSKeychainGetSecrets: Using XPC IPC calling out to Helper");
    
    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, keychain_getsecrets);

    reply_dict = SendDict_GetReply(dict);
 
    if (reply_dict != NULL)
    {
        numsecrets = xpc_dictionary_get_uint64(reply_dict, "keychain_num_secrets");
        sec = xpc_dictionary_get_data(reply_dict, "keychain_secrets", &secrets_size);
        secretsCnt = xpc_dictionary_get_uint64(reply_dict, "keychain_secrets_count");
        error_code = xpc_dictionary_get_int64(reply_dict,   kHelperErrCode);
    }
 
    mDNSHELPER_DEBUG("mDNSKeychainGetSecrets: Using XPC IPC calling out to Helper: numsecrets is %d, secretsCnt is %d error_code is %d secret_size is %d",
           numsecrets, secretsCnt, error_code, secrets_size);
     
    if (NULL == (bytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (void*)sec, secretsCnt, kCFAllocatorNull)))
    {
        error_code = kHelperErr_ApiErr;
        LogMsg("mDNSKeychainGetSecrets: CFDataCreateWithBytesNoCopy failed");
        goto fin;
    }
    
    if (NULL == (plist = CFPropertyListCreateWithData(kCFAllocatorDefault, bytes, kCFPropertyListImmutable, NULL, NULL)))
    {
        error_code = kHelperErr_ApiErr;
        LogMsg("mDNSKeychainGetSecrets: CFPropertyListCreateFromXMLData failed");
        goto fin;
    }
    
    if (CFArrayGetTypeID() != CFGetTypeID(plist))
    {
        error_code = kHelperErr_ApiErr;
        LogMsg("mDNSKeychainGetSecrets: Unexpected result type");
        CFRelease(plist);
        plist = NULL;
        goto fin;
    }
    
    *result = (CFArrayRef)plist;
    
    
fin:
    if (bytes)
        CFRelease(bytes);
    if (dict)
        xpc_release(dict);
    if (reply_dict)
        xpc_release(reply_dict);
    
    dict = NULL;
    reply_dict = NULL;
    
    return error_code;
}


int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
                          v6addr_t local_outer, short local_port, v6addr_t remote_inner,
                          v6addr_t remote_outer, short remote_port, const char* const prefix, const domainname *const fqdn)
{
    
    int err_code = kHelperErr_NotConnected;
    
    mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC calling out to Helper. Parameters are repdel[%d], lport[%d], rport[%d], prefix[%s], fqdn[%##s]",
                      replacedelete, local_port, remote_port, prefix, fqdn->c);
    
    
    char buf1[INET6_ADDRSTRLEN];
    char buf2[INET6_ADDRSTRLEN];
    char buf3[INET6_ADDRSTRLEN];
    char buf4[INET6_ADDRSTRLEN];
    
    buf1[0] = 0;
    buf2[0] = 0;
    buf3[0] = 0;
    buf4[0] = 0;
    
    inet_ntop(AF_INET6, local_inner, buf1, sizeof(buf1));
    inet_ntop(AF_INET6, local_outer, buf2, sizeof(buf2));
    inet_ntop(AF_INET6, remote_inner, buf3, sizeof(buf3));
    inet_ntop(AF_INET6, remote_outer, buf4, sizeof(buf4));
    
    char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars
    if (fqdn)
    {
        mDNSPlatformStrCopy(fqdnStr, prefix);
        ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix));
    }
    
    mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC calling out to Helper: Parameters are local_inner is %s, local_outeris %s, remote_inner is %s, remote_outer is %s",
                      buf1, buf2, buf3, buf4);
    
    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, autotunnel_setkeys);
    
    xpc_dictionary_set_data(dict, "autotunnelsetkeys_localinner",  (uint8_t*)local_inner,  sizeof(v6addr_t));
    xpc_dictionary_set_data(dict, "autotunnelsetkeys_localouter",  (uint8_t*)local_outer,  sizeof(v6addr_t));
    xpc_dictionary_set_data(dict, "autotunnelsetkeys_remoteinner", (uint8_t*)remote_inner, sizeof(v6addr_t));
    xpc_dictionary_set_data(dict, "autotunnelsetkeys_remoteouter", (uint8_t*)remote_outer, sizeof(v6addr_t));
    
    xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_lport",  local_port);
    xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_rport",  remote_port);
    xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_repdel", replacedelete);

    // xpc_dictionary_set_string(dict, "autotunnelsetkeys_prefix",  prefix);
    xpc_dictionary_set_string(dict, "autotunnelsetkeys_fqdnStr", fqdnStr);
    
    err_code = SendDict_ToServer(dict);
    
    xpc_release(dict);
    dict = NULL;

    mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC returning error_code %d", err_code);
    
    mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: this should NOT be done in mDNSResponder/Helper. For future we shall be using <rdar://problem/13792729>");
    return err_code;
}

void mDNSSendWakeupPacket(unsigned int ifid, char *eth_addr, char *ip_addr, int iteration)
{
    // (void) ip_addr; // unused
    // (void) iteration; // unused

    mDNSHELPER_DEBUG("mDNSSendWakeupPacket: Entered ethernet address[%s],ip_address[%s], interface_id[%d], iteration[%d]",
           eth_addr, ip_addr, ifid, iteration);
    
    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, send_wakepkt);
    
    xpc_dictionary_set_uint64(dict, "interface_index", ifid);
    xpc_dictionary_set_string(dict, "ethernet_address", eth_addr);
    xpc_dictionary_set_string(dict, "ip_address", ip_addr);
    xpc_dictionary_set_uint64(dict, "swp_iteration", iteration);
    
    SendDict_ToServer(dict);
    xpc_release(dict);
    dict = NULL;

}

void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray)
{
    struct
    {
        pfArray_t portArray;
        pfArray_t protocolArray;
    } pfa;
    
    mDNSPlatformMemCopy(pfa.portArray, portArray, sizeof(pfArray_t));
    mDNSPlatformMemCopy(pfa.protocolArray, protocolArray, sizeof(pfArray_t));

    mDNSHELPER_DEBUG("mDNSPacketFilterControl: XPC IPC, ifname %s", ifname);
    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, p2p_packetfilter);
    
    xpc_dictionary_set_uint64(dict, "pf_opcode", command);
    if (ifname)
        xpc_dictionary_set_string(dict, "pf_ifname", ifname);
    xpc_dictionary_set_uint64(dict, "pf_count", count);

    xpc_dictionary_set_uint64(dict, "pf_port0", pfa.portArray[0]);
    xpc_dictionary_set_uint64(dict, "pf_port1", pfa.portArray[1]);
    xpc_dictionary_set_uint64(dict, "pf_port2", pfa.portArray[2]);
    xpc_dictionary_set_uint64(dict, "pf_port3", pfa.portArray[3]);
    
    xpc_dictionary_set_uint64(dict, "pf_protocol0", pfa.protocolArray[0]);
    xpc_dictionary_set_uint64(dict, "pf_protocol1", pfa.protocolArray[1]);
    xpc_dictionary_set_uint64(dict, "pf_protocol2", pfa.protocolArray[2]);
    xpc_dictionary_set_uint64(dict, "pf_protocol3", pfa.protocolArray[3]);
    SendDict_ToServer(dict);
    xpc_release(dict);
    dict = NULL;
    
    mDNSHELPER_DEBUG("mDNSPacketFilterControl: portArray0[%d] portArray1[%d] portArray2[%d] portArray3[%d] protocolArray0[%d] protocolArray1[%d] protocolArray2[%d] protocolArray3[%d]",
            pfa.portArray[0], pfa.portArray[1], pfa.portArray[2], pfa.portArray[3], pfa.protocolArray[0], pfa.protocolArray[1], pfa.protocolArray[2], pfa.protocolArray[3]);
    
}

void mDNSSendKeepalive(const v6addr_t sadd, const v6addr_t dadd, uint16_t lport, uint16_t rport, uint32_t seq, uint32_t ack, uint16_t win)
{

    mDNSHELPER_DEBUG("mDNSSendKeepalive: Using XPC IPC calling out to Helper: lport is[%d] rport is[%d] seq is[%d] ack is[%d] win is[%d]",
           lport, rport, seq, ack, win);
    
    char buf1[INET6_ADDRSTRLEN];
    char buf2[INET6_ADDRSTRLEN];
    
    buf1[0] = 0;
    buf2[0] = 0;
    
    inet_ntop(AF_INET6, sadd, buf1, sizeof(buf1));
    inet_ntop(AF_INET6, dadd, buf2, sizeof(buf2));
    mDNSHELPER_DEBUG("mDNSSendKeepalive: Using XPC IPC calling out to Helper: sadd is %s, dadd is %s", buf1, buf2);
    
    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, send_keepalive);
    
    xpc_dictionary_set_data(dict, "send_keepalive_sadd", (uint8_t*)sadd, sizeof(v6addr_t));
    xpc_dictionary_set_data(dict, "send_keepalive_dadd", (uint8_t*)dadd, sizeof(v6addr_t));
    
    xpc_dictionary_set_uint64(dict, "send_keepalive_lport", lport);
    xpc_dictionary_set_uint64(dict, "send_keepalive_rport", rport);
    xpc_dictionary_set_uint64(dict, "send_keepalive_seq", seq);
    xpc_dictionary_set_uint64(dict, "send_keepalive_ack", ack);
    xpc_dictionary_set_uint64(dict, "send_keepalive_win", win);
    
    SendDict_ToServer(dict);
    xpc_release(dict);
    dict = NULL;
    
}

int mDNSRetrieveTCPInfo(int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid)
{
    int error_code = kHelperErr_NotConnected;
    xpc_object_t reply_dict = NULL;
    
    mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo: Using XPC IPC calling out to Helper: lport is[%d] rport is[%d] family is[%d]",
           lport, rport, family);
    
    char buf1[INET6_ADDRSTRLEN];
    char buf2[INET6_ADDRSTRLEN];
    buf1[0] = 0;
    buf2[0] = 0;
    
    inet_ntop(AF_INET6, laddr, buf1, sizeof(buf1));
    inet_ntop(AF_INET6, raddr, buf2, sizeof(buf2));
    mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo:: Using XPC IPC calling out to Helper: laddr is %s, raddr is %s", buf1, buf2);
    
    Init_Connection(kHelperService);
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    xpc_dictionary_set_uint64(dict, kHelperMode, retreive_tcpinfo);
    
    xpc_dictionary_set_data(dict, "retreive_tcpinfo_laddr", (uint8_t*)laddr, sizeof(v6addr_t));
    xpc_dictionary_set_data(dict, "retreive_tcpinfo_raddr", (uint8_t*)raddr, sizeof(v6addr_t));
    
    xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_family", family);
    xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_lport", lport);
    xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_rport", rport);
    
    reply_dict = SendDict_GetReply(dict);
    
    if (reply_dict != NULL)
    {
        *seq = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_seq");
        *ack = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_ack");
        *win = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_win");
        *intfid = (int32_t)xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_ifid");
        error_code = xpc_dictionary_get_int64(reply_dict, kHelperErrCode);
    }
    
    mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo: Using XPC IPC calling out to Helper: seq is %d, ack is %d, win is %d, intfid is %d, error is %d",
           *seq, *ack, *win, *intfid, error_code);
    
    if (dict)
        xpc_release(dict);
    if (reply_dict)
        xpc_release(reply_dict);
    dict = NULL;
    reply_dict = NULL;

    return error_code;
}
