]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2012-2020 Apple Inc. All rights reserved. | |
3 | * | |
4 | * PRIVATE DNSX CLIENT LIBRARY --FOR Apple Platforms ONLY OSX/iOS-- | |
5 | * Resides in /usr/lib/libdns_services.dylib | |
6 | */ | |
7 | ||
8 | #include "dns_services.h" | |
9 | #include <xpc/xpc.h> | |
10 | #include <Block.h> | |
11 | #include <os/log.h> | |
12 | #include <stdatomic.h> | |
13 | #include "xpc_clients.h" | |
14 | ||
15 | //************************************************************************************************************* | |
16 | // Globals | |
17 | ||
18 | #define connection_t xpc_connection_t | |
19 | ||
20 | struct _DNSXConnRef_t | |
21 | { | |
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. | |
27 | }; | |
28 | ||
29 | //************************************************************************************************************* | |
30 | // Utility Functions | |
31 | ||
32 | static bool LogDebugEnabled() | |
33 | { | |
34 | return true; | |
35 | } | |
36 | ||
37 | static void LogDebug(const char *prefix, xpc_object_t o) | |
38 | { | |
39 | if (!LogDebugEnabled()) | |
40 | return; | |
41 | ||
42 | char *desc = xpc_copy_description(o); | |
43 | os_log_info(OS_LOG_DEFAULT, "%s: %s", prefix, desc); | |
44 | free(desc); | |
45 | } | |
46 | ||
47 | static void _DNSXConnRefRetain(DNSXConnRef connRef) | |
48 | { | |
49 | atomic_fetch_add(&connRef->refCount, 1); | |
50 | } | |
51 | ||
52 | static void _DNSXConnRefRelease(DNSXConnRef connRef) | |
53 | { | |
54 | if (atomic_fetch_add(&connRef->refCount, -1) == 1) | |
55 | { | |
56 | free(connRef); | |
57 | } | |
58 | } | |
59 | ||
60 | //************************************************************************************************************** | |
61 | ||
62 | void DNSXRefDeAlloc(DNSXConnRef connRef) | |
63 | { | |
64 | if (connRef == NULL) | |
65 | { | |
66 | os_log(OS_LOG_DEFAULT, "dns_services: DNSXRefDeAlloc called with NULL DNSXConnRef"); | |
67 | return; | |
68 | } | |
69 | ||
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"); | |
79 | ||
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"); | |
85 | }); | |
86 | }); | |
87 | ||
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"); | |
90 | ||
91 | } | |
92 | ||
93 | // Sends the Msg(Dictionary) to the Server Daemon | |
94 | static DNSXErrorType SendMsgToServer(DNSXConnRef connRef, xpc_object_t msg) | |
95 | { | |
96 | DNSXErrorType errx = kDNSX_NoError; | |
97 | ||
98 | LogDebug("dns_services: SendMsgToServer Sending msg to Daemon", msg); | |
99 | ||
100 | _DNSXConnRefRetain(connRef); | |
101 | xpc_connection_send_message_with_reply((connRef)->conn_ref, msg, (connRef)->lib_q, ^(xpc_object_t recv_msg) | |
102 | { | |
103 | xpc_type_t type = xpc_get_type(recv_msg); | |
104 | ||
105 | if (type == XPC_TYPE_DICTIONARY) | |
106 | { | |
107 | LogDebug("dns_services: SendMsgToServer Received reply msg from Daemon", recv_msg); | |
108 | uint64_t daemon_status = xpc_dictionary_get_uint64(recv_msg, kDNSDaemonReply); | |
109 | ||
110 | if (connRef == NULL || connRef->client_q == NULL || connRef->AppCallBack == NULL) | |
111 | { | |
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); | |
114 | } | |
115 | else | |
116 | { | |
117 | switch (daemon_status) | |
118 | { | |
119 | case kDNSMsg_NoError: | |
120 | dispatch_async((connRef)->client_q, ^{ | |
121 | if (connRef->AppCallBack != NULL) | |
122 | connRef->AppCallBack(connRef, kDNSX_NoError); | |
123 | }); | |
124 | break; | |
125 | ||
126 | case kDNSMsg_Busy: | |
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); | |
131 | }); | |
132 | break; | |
133 | ||
134 | default: | |
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); | |
139 | }); | |
140 | break; | |
141 | } | |
142 | } | |
143 | } | |
144 | else | |
145 | { | |
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); | |
149 | } | |
150 | _DNSXConnRefRelease(connRef); | |
151 | }); | |
152 | ||
153 | return errx; | |
154 | } | |
155 | ||
156 | // Creates a new DNSX Connection Reference(DNSXConnRef) | |
157 | static DNSXErrorType InitConnection(DNSXConnRef *connRefOut, const char *servname, dispatch_queue_t clientq, DNSXEnableProxyReply AppCallBack) | |
158 | { | |
159 | if (connRefOut == NULL) | |
160 | { | |
161 | os_log(OS_LOG_DEFAULT, "dns_services: InitConnection() connRef cannot be NULL"); | |
162 | return kDNSX_BadParam; | |
163 | } | |
164 | ||
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)); | |
167 | if (connRef == NULL) | |
168 | { | |
169 | os_log(OS_LOG_DEFAULT, "dns_services: InitConnection() No memory to allocate!"); | |
170 | return kDNSX_NoMem; | |
171 | } | |
172 | ||
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); | |
180 | ||
181 | if (connRef->conn_ref == NULL || connRef->lib_q == NULL) | |
182 | { | |
183 | os_log(OS_LOG_DEFAULT, "dns_services: InitConnection() conn_ref/lib_q is NULL"); | |
184 | if (connRef != NULL) | |
185 | { | |
186 | _DNSXConnRefRelease(connRef); | |
187 | } | |
188 | return kDNSX_NoMem; | |
189 | } | |
190 | ||
191 | xpc_connection_set_event_handler(connRef->conn_ref, ^(xpc_object_t event) | |
192 | { | |
193 | if (connRef == NULL || connRef->client_q == NULL || connRef->AppCallBack == NULL) | |
194 | { | |
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)); | |
198 | } | |
199 | else | |
200 | { | |
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); | |
206 | }); | |
207 | } | |
208 | ||
209 | }); | |
210 | xpc_connection_resume(connRef->conn_ref); | |
211 | ||
212 | *connRefOut = connRef; | |
213 | ||
214 | return kDNSX_NoError; | |
215 | } | |
216 | ||
217 | #define kDNSXProxyRecognizedFlags (kDNSXProxyFlagForceAAAASynthesis) | |
218 | ||
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) | |
222 | { | |
223 | DNSXErrorType errx = kDNSX_NoError; | |
224 | ||
225 | // Sanity Checks | |
226 | if (connRef == NULL || callBack == NULL || clientq == NULL) | |
227 | { | |
228 | os_log(OS_LOG_DEFAULT, "dns_services: _DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter"); | |
229 | return kDNSX_BadParam; | |
230 | } | |
231 | ||
232 | // Get connRef from InitConnection() | |
233 | if (*connRef == NULL) | |
234 | { | |
235 | errx = InitConnection(connRef, kDNSProxyService, clientq, callBack); | |
236 | if (errx) // On error InitConnection() leaves *connRef set to NULL | |
237 | { | |
238 | os_log(OS_LOG_DEFAULT, "dns_services: Since InitConnection() returned %d error returning w/o sending msg", errx); | |
239 | return errx; | |
240 | } | |
241 | } | |
242 | else // Client already has a connRef and this is not valid use for this SPI | |
243 | { | |
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; | |
246 | } | |
247 | ||
248 | if (flags & ~kDNSXProxyRecognizedFlags) | |
249 | { | |
250 | os_log_error(OS_LOG_DEFAULT, "dns_services: Use of unrecognized flags 0x%08X", flags & ~kDNSXProxyRecognizedFlags); | |
251 | return kDNSX_BadParam; | |
252 | } | |
253 | ||
254 | // Create Dictionary To Send | |
255 | xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); | |
256 | if (dict == NULL) | |
257 | { | |
258 | os_log(OS_LOG_DEFAULT, "dns_services: _DNSXEnableProxy could not create the Msg Dict To Send!"); | |
259 | DNSXRefDeAlloc(*connRef); | |
260 | return kDNSX_NoMem; | |
261 | } | |
262 | ||
263 | xpc_dictionary_set_uint64(dict, kDNSProxyParameters, proxyparam); | |
264 | ||
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]); | |
270 | ||
271 | xpc_dictionary_set_uint64(dict, kDNSOutIfindex, outIfindex); | |
272 | ||
273 | if (ipv6Prefix) | |
274 | { | |
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); | |
279 | } | |
280 | errx = SendMsgToServer(*connRef, dict); | |
281 | xpc_release(dict); | |
282 | dict = NULL; | |
283 | ||
284 | return errx; | |
285 | } | |
286 | ||
287 | DNSXErrorType DNSXEnableProxy(DNSXConnRef *connRef, DNSProxyParameters proxyparam, IfIndex inIfindexArr[MaxInputIf], | |
288 | IfIndex outIfindex, dispatch_queue_t clientq, DNSXEnableProxyReply callBack) | |
289 | { | |
290 | return _DNSXEnableProxy(connRef, proxyparam, inIfindexArr, outIfindex, NULL, 0, kDNSXProxyFlagNull, clientq, callBack); | |
291 | } | |
292 | ||
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) | |
296 | { | |
297 | return _DNSXEnableProxy(connRef, proxyparam, inIfindexArr, outIfindex, ipv6Prefix, ipv6PrefixBitLen, flags, clientq, | |
298 | callBack); | |
299 | } |