summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSMacOSX/Private/xpc_services.c
blob: 7a0e29fb971de78572cd36a53271801ba99312c5 (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
250
251
252
253
254
255
/* -*- Mode: C; tab-width: 4 -*-
 *
 * Copyright (c) 2012 Apple Inc. All rights reserved.
 *
 * xpc_services.c
 * mDNSResponder 
 *
 * XPC as an IPC mechanism to communicate with Clients. Open only to Apple OSX/iOS clients
 */

#include "xpc_services.h"
#include "dns_xpc.h"

#ifndef UNICAST_DISABLED

#include "dnsproxy.h"        // DNSProxyInit/ProxyUDPCallback/ProxyTCPCallback
#include "mDNSMacOSX.h"      // KQueueLock/KQueueUnlock
#include <xpc/xpc.h>
#include <xpc/private.h>     // xpc_connection_copy_entitlement_value 

// ***************************************************************************
// Globals
extern mDNS mDNSStorage;
static int dps_client_pid; // To track current active client using DNS Proxy Service
static dispatch_queue_t dps_queue = NULL;
// ***************************************************************************

// prints current XPC Server State
mDNSexport void xpcserver_info(mDNS *const m)
{

    LogMsg("----- Active XPC Clients -----");
    if (dps_client_pid)
       LogMsg("DNSProxy->Client[%d]: InputIntfs[%d, %d, %d, %d, %d] Output[%d]", dps_client_pid, m->dp_ipintf[0],
                m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); 
}


mDNSlocal void ActivateDNSProxy(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf, mDNSBool proxy_off)
{

    LogInfo("ActivateDNSProxy: InterfaceIndex List by Client: Input[%d, %d, %d, %d, %d] Output[%d] ", IpIfArr[0], IpIfArr[1],
             IpIfArr[2], IpIfArr[3], IpIfArr[4], OpIf);
 
    KQueueLock(&mDNSStorage); 
    DNSProxyInit(&mDNSStorage, IpIfArr, OpIf);
    if (proxy_off) // Open skts only if proxy was OFF else we may end up opening extra skts
        mDNSPlatformInitDNSProxySkts(&mDNSStorage, ProxyUDPCallback, ProxyTCPCallback);
    KQueueUnlock(&mDNSStorage, "DNSProxy Activated");
}

mDNSlocal void handle_dps_terminate()
{

    LogInfo("handle_dps_terminate: Client PID[%d] terminated connection or crashed. Proceed to terminate DNSProxy", dps_client_pid);
    // Clear the Client's PID, so that we can now accept new DPS requests
    dps_client_pid = 0;

    KQueueLock(&mDNSStorage);
    mDNSPlatformCloseDNSProxySkts(&mDNSStorage);
    // TBD: Close TCP Sockets
    DNSProxyTerminate(&mDNSStorage);
    KQueueUnlock(&mDNSStorage, "DNSProxy Deactivated");
}

mDNSlocal void handle_dps_request(xpc_object_t req)
{
    int dps_tmp_client;
    mDNSBool proxy_off = mDNSfalse;
    xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req);
    dps_tmp_client = (int) xpc_connection_get_pid(remote_conn);

    LogInfo("handle_dps_request: Handler for DNS Proxy Requests");

    if (dps_client_pid <= 0)
    {
        LogInfo("handle_dps_request: DNSProxy is not engaged (New Client)");
        // No Active Client, save new Client's PID (also indicates DNS Proxy was OFF)
        dps_client_pid = dps_tmp_client;
        proxy_off = mDNStrue;        
    }
    else
    {
        // We already have an active DNS Proxy Client and until that client does not terminate the connection
        // or crashes, a new client cannot change/override the current DNS Proxy settings.
        if (dps_client_pid != dps_tmp_client)
        {
            LogMsg("handle_dps_request: A Client is already using DNS Proxy and your request cannot be handled at this time");
            // Return Engaged Status to the client
            xpc_object_t reply = xpc_dictionary_create(NULL, NULL, 0); 
            if (reply)
            {   
                xpc_dictionary_set_uint64(reply, kDNSDaemonReply, kDNSDaemonEngaged);
                xpc_connection_send_message(remote_conn, reply);
                xpc_release(reply);  
            }   
            else
            {   
                LogMsg("handle_dps_request: Reply Dictionary could not be created");
                return;
            }
            // We do not really need to terminate the connection with the client
            // as it may try again later which is fine
            return;   
        }
    }
    
    // Return Success Status to the client
    xpc_object_t response = xpc_dictionary_create(NULL, NULL, 0); 
    if (response)
    {
        xpc_dictionary_set_uint64(response, kDNSDaemonReply, kDNSMsgReceived);
        xpc_connection_send_message(remote_conn, response);
        xpc_release(response);  
    }
    else
    { 
        LogMsg("handle_dps_request: Response Dictionary could not be created");
        return;
    }

    // Proceed to get DNS Proxy Settings from the Client
    if (xpc_dictionary_get_uint64(req, kDNSProxyParameters))
    {
        mDNSu32 inIf[MaxIp], outIf;
        
        inIf[0]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex0);
        inIf[1]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex1);
        inIf[2]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex2);
        inIf[3]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex3);
        inIf[4]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex4);
        outIf     = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSOutIfindex);
        
        ActivateDNSProxy(inIf, outIf, proxy_off);
    }
    
}

// Verify Client's Entitlement
mDNSlocal mDNSBool IsEntitled(xpc_connection_t conn, const char *password)
{
    mDNSBool entitled = mDNSfalse;
    xpc_object_t ent = xpc_connection_copy_entitlement_value(conn, password);

    if (ent)
    {
        if (xpc_get_type(ent) == XPC_TYPE_BOOL && xpc_bool_get_value(ent))
        {
            entitled = mDNStrue;
        }
        xpc_release(ent);
    }
    else
    {
        LogMsg("IsEntitled: Client Entitlement is NULL");
    }
    
    return entitled;
}

mDNSlocal void accept_dps_client(xpc_connection_t conn)
{
    uid_t euid;
    euid = xpc_connection_get_euid(conn);

    if (euid != 0 || !IsEntitled(conn, kDNSProxyService))
    {   
        LogMsg("accept_dps_client: DNSProxyService Client Pid[%d] is missing Entitlement or is not root!", (int) xpc_connection_get_pid(conn));
        xpc_connection_cancel(conn);
        return; 
    }

    xpc_retain(conn);
    xpc_connection_set_target_queue(conn, dps_queue);
    xpc_connection_set_event_handler(conn, ^(xpc_object_t req_msg)
        {
            xpc_type_t type = xpc_get_type(req_msg);

            if (type == XPC_TYPE_DICTIONARY)
            {
                handle_dps_request(req_msg);
            }
            // We hit the case below only if Client Terminated DPS Connection OR Crashed
            else
            {
                LogInfo("accept_dps_client: DPS Client %p teared down the connection or Crashed", (void *) conn);
                // Only the Client that has activated DPS should be able to terminate it
                if (((int)xpc_connection_get_pid(conn)) == dps_client_pid)
                    handle_dps_terminate(); 
                xpc_release(conn);
            }
        });
    xpc_connection_resume(conn);
                
}

mDNSlocal void init_dnsproxy_service(void)
{
    
    xpc_connection_t dps_listener = xpc_connection_create_mach_service(kDNSProxyService, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
    if (!dps_listener || xpc_get_type(dps_listener) != XPC_TYPE_CONNECTION)
    {
        LogMsg("init_dnsproxy_service: Error Creating XPC Listener for DNSProxyService !!");
        return;
    }
    
    dps_queue = dispatch_queue_create("com.apple.mDNSResponder.dnsproxyservice_queue", NULL);

    xpc_connection_set_event_handler(dps_listener, ^(xpc_object_t eventmsg)
        {
            xpc_type_t type = xpc_get_type(eventmsg);

            if (type == XPC_TYPE_CONNECTION)
            {
                LogInfo("init_dnsproxy_service: New DNSProxyService Client %p", eventmsg);
                accept_dps_client(eventmsg);
            }
            // Ideally, we would never hit the cases below
            else if (type == XPC_TYPE_ERROR)
            {
                LogMsg("init_dnsproxy_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION));
                return;
            }
            else
            {
                LogMsg("init_dnsproxy_service: Unknown EventMsg type");
                return;
            }
        });
    xpc_connection_resume(dps_listener);

}

mDNSexport void xpc_server_init()
{
    // Add XPC Services here
    init_dnsproxy_service();
}

#else // !UNICAST_DISABLED

mDNSexport void xpc_server_init()
{
    return;
}

mDNSexport void xpcserver_info(mDNS *const m)
{
    (void) m;
    
    return;
}

#endif // !UNICAST_DISABLED