summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSMacOSX/Private/dns_services.c
blob: 794e252635dec4ff4d3f3ddc179dd01a9142ad97 (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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/* -*- Mode: C; tab-width: 4 -*-
 *
 * Copyright (c) 2012-2015 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 <syslog.h>

#define LOG_NOW LOG_INFO

//*************************************************************************************************************
// 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 true;
}

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

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

void DNSXRefDeAlloc(DNSXConnRef connRef)
{
    if (connRef == NULL)
    {
        syslog(LOG_WARNING, "dns_services DD: DNSXRefDeAlloc called with NULL DNSXConnRef");
        return;
    }
    
    // Schedule this work on the internal library queue
    dispatch_sync(connRef->lib_q, ^{
        xpc_connection_set_event_handler((connRef)->conn_ref, ^(__unused xpc_object_t event){}); // ignore any more events
        xpc_release(connRef->conn_ref);
        connRef->conn_ref = NULL;
        dispatch_release(connRef->lib_q);
        connRef->lib_q = NULL;
        connRef->AppCallBack = NULL;
        syslog(LOG_NOW, "dns_services DD: DNSXRefDeAlloc successfully DeAllocated conn_ref & lib_q");
        
        dispatch_async((connRef)->client_q, ^{
            dispatch_release(connRef->client_q);
            connRef->client_q = NULL;
            free(connRef);
            syslog(LOG_NOW, "dns_services DD: DNSXRefDeAlloc successfully DeAllocated client_q & freed connRef");
        });
    });
    
    // DO NOT reference connRef after this comment, as it may have been freed
    syslog(LOG_NOW, "dns_services DD: DNSXRefDeAlloc successfully DeAllocated connRef");
    
}

// Sends the Msg(Dictionary) to the Server Daemon
static DNSXErrorType SendMsgToServer(DNSXConnRef connRef, xpc_object_t msg)
{
    DNSXErrorType errx = kDNSX_NoError;
    
    LogDebug("dns_services DD: SendMsgToServer Sending msg to Daemon", msg);
    
    xpc_connection_send_message_with_reply((connRef)->conn_ref, msg, (connRef)->lib_q, ^(xpc_object_t recv_msg)
    {
        xpc_type_t type = xpc_get_type(recv_msg);
                                               
        if (type == XPC_TYPE_DICTIONARY)
        {
            LogDebug("dns_services DD: SendMsgToServer Received reply msg from Daemon", recv_msg);
            uint64_t daemon_status = xpc_dictionary_get_uint64(recv_msg, kDNSDaemonReply);
                                                   
            if (connRef == NULL || connRef->client_q == NULL || connRef->AppCallBack == NULL)
            {
                // If connRef is bad, do not schedule any callbacks to the client
                syslog(LOG_WARNING, "dns_services DD: SendMsgToServer: connRef is BAD Daemon status code [%llu]", daemon_status);
            }
            else
            {
                switch (daemon_status)
                {
                    case kDNSMsg_NoError:
                        dispatch_async((connRef)->client_q, ^{
                        if (connRef->AppCallBack != NULL)
                            ((DNSXEnableProxyReply)connRef->AppCallBack)(connRef, kDNSX_NoError);
                        });
                        break;
                                                             
                    case kDNSMsg_BadArg:
                        dispatch_async((connRef)->client_q, ^{
                        if (connRef->AppCallBack != NULL)
                            ((DNSXEnableProxyReply)connRef->AppCallBack)(connRef, kDNSX_BadParam);
                        });
                        break;
                                                               
                    default:
                        dispatch_async((connRef)->client_q, ^{
                        if (connRef->AppCallBack != NULL)
                            ((DNSXEnableProxyReply)connRef->AppCallBack)(connRef, kDNSX_UnknownErr);
                        });
                        break;
                }
            }
        }
        else
        {
            syslog(LOG_WARNING, "dns_services DD: SendMsgToServer Received unexpected reply from daemon [%s]",
                                xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION));
            LogDebug("dns_services DD: SendMsgToServer Unexpected Reply contents", recv_msg);
        }
    });
    
    return errx;
}

// Creates a new DNSX Connection Reference(DNSXConnRef)
static DNSXErrorType InitConnection(DNSXConnRef *connRefOut, const char *servname, dispatch_queue_t clientq, void *AppCallBack)
{
    if (connRefOut == NULL)
        return kDNSX_BadParam;
    
    // Use a DNSXConnRef on the stack to be captured in the blocks below, rather than capturing the DNSXConnRef* owned by the client
    DNSXConnRef connRef = malloc(sizeof(struct _DNSXConnRef_t));
    if (connRef == NULL)
    {
        syslog(LOG_WARNING, "dns_services DD: 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", DISPATCH_QUEUE_SERIAL);
    connRef->conn_ref     = xpc_connection_create_mach_service(servname, connRef->lib_q, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
    
    if (connRef->conn_ref == NULL || connRef->lib_q == NULL)
    {
        syslog(LOG_WARNING, "dns_services DD: InitConnection() conn_ref/lib_q is NULL");
        if (connRef != NULL)
            free(connRef);
        return kDNSX_NoMem;
    }
    
    xpc_connection_set_event_handler(connRef->conn_ref, ^(xpc_object_t event)
    {
        if (connRef == NULL || connRef->client_q == NULL || connRef->AppCallBack == NULL)
        {
            // If connRef is bad, do not schedule any callbacks to the client
            syslog(LOG_WARNING, "dns_services DD: InitConnection: connRef is BAD Unexpected Connection Error [%s]",
                                xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
        }
        else
        {
            syslog(LOG_WARNING, "dns_services DD: InitConnection: Unexpected Connection Error [%s] Ping the client",
                                 xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
            dispatch_async(connRef->client_q, ^{
            if (connRef->AppCallBack != NULL)
                ((DNSXEnableProxyReply)connRef->AppCallBack)(connRef, kDNSX_DaemonNotRunning);
            });
        }
                                         
    });
    xpc_connection_resume(connRef->conn_ref);
    
    *connRefOut = connRef;
    
    return kDNSX_NoError;
}

DNSXErrorType DNSXEnableProxy(DNSXConnRef *connRef, DNSProxyParameters proxyparam, IfIndex inIfindexArr[MaxInputIf],
                              IfIndex outIfindex, dispatch_queue_t clientq, DNSXEnableProxyReply callBack)
{
    
    DNSXErrorType errx = kDNSX_NoError;
    
    // Sanity Checks
    if (connRef == NULL || callBack == NULL || clientq == NULL)
    {
        syslog(LOG_WARNING, "dns_services DD: DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter");
        return kDNSX_BadParam;
    }
    
    // Get connRef from InitConnection()
    if (*connRef == NULL)
    {
        errx = InitConnection(connRef, kDNSProxyService, clientq, callBack);
        if (errx) // On error InitConnection() leaves *connRef set to NULL
        {
            syslog(LOG_WARNING, "dns_services DD: Since InitConnection() returned %d error returning w/o sending msg", errx);
            return errx;
        }
    }
    else // Client already has a connRef and this is not valid use for this SPI
    {
        syslog(LOG_WARNING, "dns_services DD: Client already has a valid connRef! This is incorrect usage from the client");
        return kDNSX_BadParam;
    }
    
    // Create Dictionary To Send
    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
    if (dict == NULL)
    {
        syslog(LOG_WARNING, "dns_services DD: DNSXEnableProxy could not create the Msg Dict To Send!");
        DNSXRefDeAlloc(*connRef);
        return kDNSX_NoMem;
    }
    
    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);
    xpc_release(dict);
    dict = NULL;
    
    return errx;
}