2 * Copyright (c) 2012-2020 Apple Inc. All rights reserved.
4 * PRIVATE DNSX CLIENT LIBRARY --FOR Apple Platforms ONLY OSX/iOS--
5 * Resides in /usr/lib/libdns_services.dylib
8 #include "dns_services.h"
12 #include <stdatomic.h>
13 #include "xpc_clients.h"
15 //*************************************************************************************************************
18 #define connection_t xpc_connection_t
22 connection_t conn_ref
; // xpc_connection between client and daemon
23 dispatch_queue_t lib_q
; // internal queue created in library itself
24 DNSXEnableProxyReply AppCallBack
; // Callback function ptr for Client
25 dispatch_queue_t client_q
; // Queue specified by client for scheduling its Callback
26 _Atomic(int32_t) refCount
; // Reference count for this object.
29 //*************************************************************************************************************
32 static bool LogDebugEnabled()
37 static void LogDebug(const char *prefix
, xpc_object_t o
)
39 if (!LogDebugEnabled())
42 char *desc
= xpc_copy_description(o
);
43 os_log_info(OS_LOG_DEFAULT
, "%s: %s", prefix
, desc
);
47 static void _DNSXConnRefRetain(DNSXConnRef connRef
)
49 atomic_fetch_add(&connRef
->refCount
, 1);
52 static void _DNSXConnRefRelease(DNSXConnRef connRef
)
54 if (atomic_fetch_add(&connRef
->refCount
, -1) == 1)
60 //**************************************************************************************************************
62 void DNSXRefDeAlloc(DNSXConnRef connRef
)
66 os_log(OS_LOG_DEFAULT
, "dns_services: DNSXRefDeAlloc called with NULL DNSXConnRef");
70 // Schedule this work on the internal library queue
71 dispatch_sync(connRef
->lib_q
, ^{
72 xpc_connection_set_event_handler((connRef
)->conn_ref
, ^(__unused xpc_object_t event
){}); // ignore any more events
73 xpc_release(connRef
->conn_ref
);
74 connRef
->conn_ref
= NULL
;
75 dispatch_release(connRef
->lib_q
);
76 connRef
->lib_q
= NULL
;
77 connRef
->AppCallBack
= NULL
;
78 os_log_info(OS_LOG_DEFAULT
, "dns_services: DNSXRefDeAlloc successfully DeAllocated conn_ref & lib_q");
80 dispatch_async((connRef
)->client_q
, ^{
81 dispatch_release(connRef
->client_q
);
82 connRef
->client_q
= NULL
;
83 _DNSXConnRefRelease(connRef
);
84 os_log_info(OS_LOG_DEFAULT
, "dns_services: DNSXRefDeAlloc successfully DeAllocated client_q & released connRef");
88 // DO NOT reference connRef after this comment, as it may have been freed
89 os_log_info(OS_LOG_DEFAULT
, "dns_services: DNSXRefDeAlloc successfully DeAllocated connRef");
93 // Sends the Msg(Dictionary) to the Server Daemon
94 static DNSXErrorType
SendMsgToServer(DNSXConnRef connRef
, xpc_object_t msg
)
96 DNSXErrorType errx
= kDNSX_NoError
;
98 LogDebug("dns_services: SendMsgToServer Sending msg to Daemon", msg
);
100 _DNSXConnRefRetain(connRef
);
101 xpc_connection_send_message_with_reply((connRef
)->conn_ref
, msg
, (connRef
)->lib_q
, ^(xpc_object_t recv_msg
)
103 xpc_type_t type
= xpc_get_type(recv_msg
);
105 if (type
== XPC_TYPE_DICTIONARY
)
107 LogDebug("dns_services: SendMsgToServer Received reply msg from Daemon", recv_msg
);
108 uint64_t daemon_status
= xpc_dictionary_get_uint64(recv_msg
, kDNSDaemonReply
);
110 if (connRef
== NULL
|| connRef
->client_q
== NULL
|| connRef
->AppCallBack
== NULL
)
112 // If connRef is bad, do not schedule any callbacks to the client
113 os_log(OS_LOG_DEFAULT
, "dns_services: SendMsgToServer: connRef is BAD Daemon status code [%llu]", daemon_status
);
117 switch (daemon_status
)
119 case kDNSMsg_NoError
:
120 dispatch_async((connRef
)->client_q
, ^{
121 if (connRef
->AppCallBack
!= NULL
)
122 connRef
->AppCallBack(connRef
, kDNSX_NoError
);
127 os_log(OS_LOG_DEFAULT
, "dns_services: SendMsgToServer: DNS Proxy already in use");
128 dispatch_async((connRef
)->client_q
, ^{
129 if (connRef
->AppCallBack
!= NULL
)
130 connRef
->AppCallBack(connRef
, kDNSX_Busy
);
135 os_log(OS_LOG_DEFAULT
, "dns_services: SendMsgToServer: Unknown error");
136 dispatch_async((connRef
)->client_q
, ^{
137 if (connRef
->AppCallBack
!= NULL
)
138 connRef
->AppCallBack(connRef
, kDNSX_UnknownErr
);
146 os_log(OS_LOG_DEFAULT
, "dns_services: SendMsgToServer Received unexpected reply from daemon [%s]",
147 xpc_dictionary_get_string(recv_msg
, XPC_ERROR_KEY_DESCRIPTION
));
148 LogDebug("dns_services: SendMsgToServer Unexpected Reply contents", recv_msg
);
150 _DNSXConnRefRelease(connRef
);
156 // Creates a new DNSX Connection Reference(DNSXConnRef)
157 static DNSXErrorType
InitConnection(DNSXConnRef
*connRefOut
, const char *servname
, dispatch_queue_t clientq
, DNSXEnableProxyReply AppCallBack
)
159 if (connRefOut
== NULL
)
161 os_log(OS_LOG_DEFAULT
, "dns_services: InitConnection() connRef cannot be NULL");
162 return kDNSX_BadParam
;
165 // Use a DNSXConnRef on the stack to be captured in the blocks below, rather than capturing the DNSXConnRef* owned by the client
166 DNSXConnRef connRef
= (DNSXConnRef
)calloc(1, sizeof(*connRef
));
169 os_log(OS_LOG_DEFAULT
, "dns_services: InitConnection() No memory to allocate!");
173 // Initialize the DNSXConnRef
174 atomic_init(&connRef
->refCount
, 1);
175 dispatch_retain(clientq
);
176 connRef
->client_q
= clientq
;
177 connRef
->AppCallBack
= AppCallBack
;
178 connRef
->lib_q
= dispatch_queue_create("com.apple.mDNSResponder.libdns_services.q", DISPATCH_QUEUE_SERIAL
);
179 connRef
->conn_ref
= xpc_connection_create_mach_service(servname
, connRef
->lib_q
, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
);
181 if (connRef
->conn_ref
== NULL
|| connRef
->lib_q
== NULL
)
183 os_log(OS_LOG_DEFAULT
, "dns_services: InitConnection() conn_ref/lib_q is NULL");
186 _DNSXConnRefRelease(connRef
);
191 xpc_connection_set_event_handler(connRef
->conn_ref
, ^(xpc_object_t event
)
193 if (connRef
== NULL
|| connRef
->client_q
== NULL
|| connRef
->AppCallBack
== NULL
)
195 // If connRef is bad, do not schedule any callbacks to the client
196 os_log(OS_LOG_DEFAULT
, "dns_services: InitConnection: connRef is BAD Unexpected Connection Error [%s]",
197 xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
201 os_log(OS_LOG_DEFAULT
, "dns_services: InitConnection: Unexpected Connection Error [%s] Ping the client",
202 xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
203 dispatch_async(connRef
->client_q
, ^{
204 if (connRef
->AppCallBack
!= NULL
)
205 connRef
->AppCallBack(connRef
, kDNSX_DaemonNotRunning
);
210 xpc_connection_resume(connRef
->conn_ref
);
212 *connRefOut
= connRef
;
214 return kDNSX_NoError
;
217 #define kDNSXProxyRecognizedFlags (kDNSXProxyFlagForceAAAASynthesis)
219 static DNSXErrorType
_DNSXEnableProxy(DNSXConnRef
*connRef
, DNSProxyParameters proxyparam
, IfIndex inIfindexArr
[MaxInputIf
],
220 IfIndex outIfindex
, uint8_t ipv6Prefix
[16], int ipv6PrefixBitLen
,
221 DNSXProxyFlags flags
, dispatch_queue_t clientq
, DNSXEnableProxyReply callBack
)
223 DNSXErrorType errx
= kDNSX_NoError
;
226 if (connRef
== NULL
|| callBack
== NULL
|| clientq
== NULL
)
228 os_log(OS_LOG_DEFAULT
, "dns_services: _DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter");
229 return kDNSX_BadParam
;
232 // Get connRef from InitConnection()
233 if (*connRef
== NULL
)
235 errx
= InitConnection(connRef
, kDNSProxyService
, clientq
, callBack
);
236 if (errx
) // On error InitConnection() leaves *connRef set to NULL
238 os_log(OS_LOG_DEFAULT
, "dns_services: Since InitConnection() returned %d error returning w/o sending msg", errx
);
242 else // Client already has a connRef and this is not valid use for this SPI
244 os_log(OS_LOG_DEFAULT
, "dns_services: Client already has a valid connRef! This is incorrect usage from the client");
245 return kDNSX_BadParam
;
248 if (flags
& ~kDNSXProxyRecognizedFlags
)
250 os_log_error(OS_LOG_DEFAULT
, "dns_services: Use of unrecognized flags 0x%08X", flags
& ~kDNSXProxyRecognizedFlags
);
251 return kDNSX_BadParam
;
254 // Create Dictionary To Send
255 xpc_object_t dict
= xpc_dictionary_create(NULL
, NULL
, 0);
258 os_log(OS_LOG_DEFAULT
, "dns_services: _DNSXEnableProxy could not create the Msg Dict To Send!");
259 DNSXRefDeAlloc(*connRef
);
263 xpc_dictionary_set_uint64(dict
, kDNSProxyParameters
, proxyparam
);
265 xpc_dictionary_set_uint64(dict
, kDNSInIfindex0
, inIfindexArr
[0]);
266 xpc_dictionary_set_uint64(dict
, kDNSInIfindex1
, inIfindexArr
[1]);
267 xpc_dictionary_set_uint64(dict
, kDNSInIfindex2
, inIfindexArr
[2]);
268 xpc_dictionary_set_uint64(dict
, kDNSInIfindex3
, inIfindexArr
[3]);
269 xpc_dictionary_set_uint64(dict
, kDNSInIfindex4
, inIfindexArr
[4]);
271 xpc_dictionary_set_uint64(dict
, kDNSOutIfindex
, outIfindex
);
275 xpc_dictionary_set_data(dict
, kDNSProxyDNS64IPv6Prefix
, ipv6Prefix
, 16);
276 xpc_dictionary_set_int64(dict
, kDNSProxyDNS64IPv6PrefixBitLen
, ipv6PrefixBitLen
);
277 const bool forceAAAASynthesis
= (flags
& kDNSXProxyFlagForceAAAASynthesis
) ? true : false;
278 xpc_dictionary_set_bool(dict
, kDNSProxyDNS64ForceAAAASynthesis
, forceAAAASynthesis
);
280 errx
= SendMsgToServer(*connRef
, dict
);
287 DNSXErrorType
DNSXEnableProxy(DNSXConnRef
*connRef
, DNSProxyParameters proxyparam
, IfIndex inIfindexArr
[MaxInputIf
],
288 IfIndex outIfindex
, dispatch_queue_t clientq
, DNSXEnableProxyReply callBack
)
290 return _DNSXEnableProxy(connRef
, proxyparam
, inIfindexArr
, outIfindex
, NULL
, 0, kDNSXProxyFlagNull
, clientq
, callBack
);
293 DNSXErrorType
DNSXEnableProxy64(DNSXConnRef
*connRef
, DNSProxyParameters proxyparam
, IfIndex inIfindexArr
[MaxInputIf
],
294 IfIndex outIfindex
, uint8_t ipv6Prefix
[16], int ipv6PrefixBitLen
, DNSXProxyFlags flags
,
295 dispatch_queue_t clientq
, DNSXEnableProxyReply callBack
)
297 return _DNSXEnableProxy(connRef
, proxyparam
, inIfindexArr
, outIfindex
, ipv6Prefix
, ipv6PrefixBitLen
, flags
, clientq
,