summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSMacOSX/Private/dns_services.c
blob: d0e9e6ca48b6ef3d288b949c084f7f71d679680f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/* -*- Mode: C; tab-width: 4 -*-
 *
 * Copyright (c) 2012 Apple Inc. All rights reserved.
 *
 * PRIVATE DNSX CLIENT LIBRARY --FOR Apple Platforms ONLY OSX/iOS--
 * Resides in /usr/lib/libdns_services.dylib
 */

#include "dns_services.h"
#include "dns_xpc.h"
#include <xpc/xpc.h>
#include <Block.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>

//*************************************************************************************************************
// Globals

#define connection_t xpc_connection_t

struct _DNSXConnRef_t
{
    connection_t      conn_ref;      // xpc_connection between client and daemon
    dispatch_queue_t  lib_q;         // internal queue created in library itself   
    void              *AppCallBack;  // Callback function ptr for Client
    dispatch_queue_t  client_q;      // Queue specified by client for scheduling its Callback
};

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

static bool LogDebugEnabled()
{
    return false;
}

static void LogDebug(const char *prefix, xpc_object_t o)
{
    if (!LogDebugEnabled()) 
        return;
    
    char *desc = xpc_copy_description(o);
    syslog(LOG_INFO, "%s: %s", prefix, desc); 
    free(desc);
}

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

void DNSXRefDeAlloc(DNSXConnRef connRef)
{
    if (!connRef)
    {
        syslog(LOG_WARNING, "dns_services: DNSXRefDeAlloc called with NULL DNSXConnRef");
        return;
    }

    // Schedule this work on the internal library queue
    dispatch_sync(connRef->lib_q, ^{

        xpc_release(connRef->conn_ref);
        connRef->AppCallBack = NULL;
        dispatch_release(connRef->client_q);

    });

    dispatch_release(connRef->lib_q);
    free(connRef);

    syslog(LOG_INFO, "dns_services: DNSXRefDeAlloc successfully DeAllocated connRef");

}

// Sends the Msg(Dictionary) to the Server
static DNSXErrorType SendMsgToServer(DNSXConnRef *connRef, xpc_object_t msg, bool old_conn)
{
    DNSXErrorType errx = kDNSX_NoError;

    LogDebug("dns_services: SendMsgToServer", msg);
    
    xpc_connection_set_event_handler((*connRef)->conn_ref, ^(xpc_object_t recv_msg)
    {
        xpc_type_t type = xpc_get_type(recv_msg);

        if (type == XPC_TYPE_DICTIONARY)
        {
            LogDebug("dns_services: SendMsgToServer SUCCESS CALLBACK FROM SERVER", recv_msg);
            syslog(LOG_INFO, "dns_services: Successfully Sent Msg to the Daemon");
            uint64_t daemon_status = xpc_dictionary_get_uint64(recv_msg, kDNSDaemonReply);
 
            // Schedule the AppCallBacks on the Client Specified Queue
            switch (daemon_status)
            {   
                case kDNSDaemonEngaged:
                        dispatch_async((*connRef)->client_q, ^{  
                                        ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_Engaged);
                                        }); 
                                        break;
                case kDNSMsgReceived:
                        dispatch_async((*connRef)->client_q, ^{
                                        ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_NoError);
                                        }); 
                                        break;
                default:
                        dispatch_async((*connRef)->client_q, ^{
                                        ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_UnknownErr);
                                        }); 
                                        break;
            }   

        }
        else
        {
            LogDebug("dns_services: SendMsgToServer UNEXPECTED CALLBACK FROM SERVER", recv_msg);
            syslog(LOG_WARNING, "dns_services: Connection failed since NO privileges to access service OR Daemon NOT Running");
            dispatch_async((*connRef)->client_q, ^{
                            ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_DaemonNotRunning);
                            });
        }
    });
    
    // To prevent Over-Resume of a connection
    if (!old_conn)
        xpc_connection_resume((*connRef)->conn_ref);
    xpc_connection_send_message((*connRef)->conn_ref, msg);
    if (!errx)
        syslog(LOG_INFO, "dns_services: SendMSgToServer sent Msg Dict successfully to Daemon");
    return errx;
}

// Creates a new DNSX Connection Reference(DNSXConnRef).
// If DNSXConnRef exists, you may want to use that depending on the use case
static DNSXErrorType InitConnection(DNSXConnRef *connRef, const char *servname, dispatch_queue_t clientq, void *AppCallBack)
{
    if (!connRef)
    {
        syslog(LOG_WARNING, "dns_services: InitConnection() called with NULL DNSXConnRef");
        return kDNSX_BadParam;   
    }

    *connRef = malloc(sizeof(struct _DNSXConnRef_t));
    if (!(*connRef))
    {
        syslog(LOG_WARNING, "dns_services: InitConnection() No memory to allocate");
        return kDNSX_NoMem;
    }

    // Initialize the DNSXConnRef  
    dispatch_retain(clientq);
    (*connRef)->client_q     = clientq;
    (*connRef)->AppCallBack  = AppCallBack;    
    (*connRef)->lib_q        = dispatch_queue_create("com.apple.mDNSResponder.libdns_services.q", NULL); 
    (*connRef)->conn_ref     = xpc_connection_create_mach_service(servname, (*connRef)->lib_q, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);

    syslog(LOG_INFO, "dns_services: InitConnection() successfully create a new DNSXConnRef");
    return kDNSX_NoError;
}

DNSXErrorType DNSXEnableProxy(DNSXConnRef *connRef, DNSProxyParameters proxyparam, IfIndex inIfindexArr[MaxInputIf], 
                               IfIndex outIfindex, dispatch_queue_t clientq, DNSXEnableProxyReply callBack)
{

    DNSXErrorType errx = kDNSX_NoError;
    bool old_conn = false;    

    // Sanity Checks
    if (!connRef || !callBack || !clientq)
    {
        syslog(LOG_WARNING, "dns_services: DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter");
        return kDNSX_BadParam;
    }   

    // If no connRef, get it from InitConnection()
    if (!*connRef)
    {
        errx = InitConnection(connRef, kDNSProxyService, clientq, callBack);
        if (errx) // On error InitConnection() leaves *connRef set to NULL
        {
            syslog(LOG_WARNING, "dns_services: Since InitConnection() returned %d error returning w/o sending msg", errx);
            return errx;
        }
    }
    else // Client already has a valid connRef
    {
        old_conn = true;
    }

    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); 
    if (!dict)
    {
        syslog(LOG_WARNING, "dns_services: DNSXEnableProxy could not create the Msg Dict To Send!");
        DNSXRefDeAlloc(*connRef);
        return kDNSX_DictError;
    }

    xpc_dictionary_set_uint64(dict, kDNSProxyParameters, proxyparam);

    xpc_dictionary_set_uint64(dict, kDNSInIfindex0,      inIfindexArr[0]);
    xpc_dictionary_set_uint64(dict, kDNSInIfindex1,      inIfindexArr[1]);
    xpc_dictionary_set_uint64(dict, kDNSInIfindex2,      inIfindexArr[2]); 
    xpc_dictionary_set_uint64(dict, kDNSInIfindex3,      inIfindexArr[3]);
    xpc_dictionary_set_uint64(dict, kDNSInIfindex4,      inIfindexArr[4]);

    xpc_dictionary_set_uint64(dict, kDNSOutIfindex,      outIfindex);
 
    errx = SendMsgToServer(connRef, dict, old_conn);
    xpc_release(dict);

    return errx; 
}