]> git.saurik.com Git - apple/configd.git/blob - dnsinfo/dnsinfo_server.c
291db1ee1bc8b66bba16cdd8d14557aa2a132828
[apple/configd.git] / dnsinfo / dnsinfo_server.c
1 /*
2 * Copyright (c) 2004-2008, 2011-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * March 9, 2004 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31 #include <notify.h>
32 #include <sysexits.h>
33 #include <syslog.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <servers/bootstrap.h>
37 #include <mach/mach.h>
38 #include <bsm/libbsm.h>
39 #include <dispatch/dispatch.h>
40 #include <xpc/xpc.h>
41 #include <CoreFoundation/CoreFoundation.h>
42 #include <SystemConfiguration/SCPrivate.h>
43
44 #include "libSystemConfiguration_client.h"
45 #include "libSystemConfiguration_server.h"
46
47 #include "dnsinfo_create.h"
48 #include "dnsinfo_server.h"
49 #include "dnsinfo_private.h"
50
51 #ifdef SC_LOG_HANDLE
52 #include <os/log.h>
53 os_log_t SC_LOG_HANDLE;
54 #endif //SC_LOG_HANDLE
55
56
57 #pragma mark -
58 #pragma mark Globals
59
60
61 /*
62 * S_dns_info
63 *
64 * Note: all accesses should be made while running on the _dns_server_queue()
65 */
66 static libSC_info_server_t S_dns_info;
67
68
69 /*
70 * S_sync_handler
71 * ACK (in-sync or not-in-sync) updates should be posted using
72 * this handler
73 *
74 * Note: all accesses should be made while running on the _dns_server_queue()
75 */
76 static _dns_sync_handler_t S_sync_handler = NULL;
77
78
79 #pragma mark -
80 #pragma mark Support functions
81
82
83 #pragma mark -
84 #pragma mark DNS configuration server
85
86
87 static dispatch_queue_t
88 _dnsinfo_server_queue()
89 {
90 static dispatch_once_t once;
91 static dispatch_queue_t q;
92
93 dispatch_once(&once, ^{
94 q = dispatch_queue_create(DNSINFO_SERVICE_NAME ".server", NULL);
95 });
96
97 return q;
98 }
99
100
101 /*
102 * _dnsinfo_copy
103 *
104 * Called when a client wants a copy of the current
105 * DNS configuration
106 *
107 * - caller must be running on the _dns_server_queue()
108 */
109 static void
110 _dnsinfo_copy(xpc_connection_t connection, xpc_object_t request)
111 {
112 CFDataRef data;
113 uint64_t generation;
114 const char *proc_name;
115 xpc_connection_t remote;
116 xpc_object_t reply;
117
118 remote = xpc_dictionary_get_remote_connection(request);
119 reply = xpc_dictionary_create_reply(request);
120 if (reply == NULL) {
121 SC_log(LOG_ERR, "<%p> _dnsinfo_copy: xpc_dictionary_create_reply: failed",
122 connection);
123 return;
124 }
125
126 // extract data and generation #
127 data = _libSC_info_server_get_data(&S_dns_info,
128 connection,
129 &generation);
130
131 // extract process name
132 proc_name = xpc_dictionary_get_string(request, DNSINFO_PROC_NAME);
133 if (proc_name == NULL) {
134 proc_name = "???";
135 }
136
137 SC_log(LOG_DEBUG, "<%p:%s[%d]> DNS configuration copy: %llu",
138 connection,
139 proc_name,
140 xpc_connection_get_pid(connection),
141 generation);
142
143 // return the DNS configuration (if available)
144 if (data != NULL) {
145 xpc_dictionary_set_data(reply,
146 DNSINFO_CONFIGURATION,
147 CFDataGetBytePtr(data),
148 CFDataGetLength(data));
149 }
150
151 // reply
152 xpc_connection_send_message(remote, reply);
153 xpc_release(reply);
154
155 return;
156 }
157
158
159 /*
160 * _dnsinfo_acknowledge
161 *
162 * Called when a client wants to acknowledge processing
163 * of the DNS configuration
164 *
165 * - caller must be running on the _dns_server_queue()
166 */
167 static void
168 _dnsinfo_acknowledge(xpc_connection_t connection, xpc_object_t request)
169 {
170 uint64_t generation;
171 Boolean inSync;
172
173 generation = xpc_dictionary_get_uint64(request, DNSINFO_GENERATION);
174
175 SC_log(LOG_DEBUG, "<%p:%d> DNS configuration ack: %llu",
176 connection,
177 xpc_connection_get_pid(connection),
178 generation);
179
180 (void) _libSC_info_server_acknowledged(&S_dns_info, connection, generation);
181
182 // Note: all of the DNS service ack's should result
183 // in a [new] network change being posted
184
185 inSync = _libSC_info_server_in_sync(&S_dns_info);
186 if (S_sync_handler != NULL) {
187 S_sync_handler(inSync);
188 }
189 return;
190 }
191
192
193 static void
194 process_request(xpc_connection_t connection, xpc_object_t request)
195 {
196 int64_t op;
197
198 op = xpc_dictionary_get_int64(request, DNSINFO_REQUEST);
199 switch (op) {
200 case DNSINFO_REQUEST_COPY :
201 /*
202 * Return the DNS configuration
203 */
204 _dnsinfo_copy(connection, request);
205 break;
206
207 case DNSINFO_REQUEST_ACKNOWLEDGE :
208 /*
209 * Acknowlege a [processed] DNS configuration
210 */
211 _dnsinfo_acknowledge(connection, request);
212
213 break;
214 default :
215 SC_log(LOG_ERR, "<%p> unknown request : %lld",
216 connection,
217 op);
218
219 break;
220 }
221
222 return;
223 }
224
225
226 static void
227 process_new_connection(xpc_connection_t c)
228 {
229 SC_log(LOG_DEBUG, "<%p:%d> DNS configuration session: open",
230 c,
231 xpc_connection_get_pid(c));
232
233 _libSC_info_server_open(&S_dns_info, c);
234
235 xpc_connection_set_target_queue(c, _dnsinfo_server_queue());
236
237 xpc_connection_set_event_handler(c, ^(xpc_object_t xobj) {
238 xpc_type_t type;
239
240 type = xpc_get_type(xobj);
241 if (type == XPC_TYPE_DICTIONARY) {
242 // process the request
243 process_request(c, xobj);
244
245 } else if (type == XPC_TYPE_ERROR) {
246 const char *desc;
247
248 desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
249 if (xobj == XPC_ERROR_CONNECTION_INVALID) {
250 Boolean changed;
251
252 SC_log(LOG_DEBUG, "<%p:%d> DNS configuration session: close",
253 c,
254 xpc_connection_get_pid(c));
255
256 changed = _libSC_info_server_close(&S_dns_info, c);
257 if (changed) {
258 Boolean inSync;
259
260 // report change
261 inSync = _libSC_info_server_in_sync(&S_dns_info);
262 if (S_sync_handler != NULL) {
263 S_sync_handler(inSync);
264 }
265 }
266
267 } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) {
268 SC_log(LOG_ERR, "<%p:%d> %s",
269 c,
270 xpc_connection_get_pid(c),
271 desc);
272
273 } else {
274 SC_log(LOG_ERR, "<%p:%d> Connection error: %p : %s",
275 c,
276 xpc_connection_get_pid(c),
277 xobj,
278 desc);
279 }
280
281 } else {
282 SC_log(LOG_ERR, "<%p:%d> unknown event type : %p",
283 c,
284 xpc_connection_get_pid(c),
285 type);
286 }
287 });
288
289 xpc_connection_resume(c);
290
291 return;
292 }
293
294
295 #pragma mark -
296 #pragma mark DNS configuration server SPIs
297
298
299 __private_extern__
300 void
301 load_DNSConfiguration(CFBundleRef bundle,
302 _dns_sync_handler_t syncHandler)
303 {
304 xpc_connection_t c;
305 const char *name;
306
307 /*
308 * keep track of DNS configuration acknowledgements
309 */
310 _libSC_info_server_init(&S_dns_info);
311
312 /*
313 * save the in-sync/not-in-sync handler
314 */
315 S_sync_handler = Block_copy(syncHandler);
316
317 // create XPC listener
318 name = getenv(DNSINFO_SERVICE_NAME);
319 if (name == NULL) {
320 name = DNSINFO_SERVICE_NAME;
321 }
322
323 c = xpc_connection_create_mach_service(name,
324 _dnsinfo_server_queue(),
325 XPC_CONNECTION_MACH_SERVICE_LISTENER);
326
327 xpc_connection_set_event_handler(c, ^(xpc_object_t event) {
328 xpc_type_t type;
329
330 type = xpc_get_type(event);
331 if (type == XPC_TYPE_CONNECTION) {
332 process_new_connection(event);
333
334 } else if (type == XPC_TYPE_ERROR) {
335 const char *desc;
336
337 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
338 if (event == XPC_ERROR_CONNECTION_INVALID) {
339 SC_log(LOG_ERR, "DNS configuration server: %s", desc);
340 xpc_release(c);
341 } else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
342 SC_log(LOG_ERR, "DNS configuration server: %s", desc);
343 } else {
344 SC_log(LOG_ERR, "DNS configuration server: Connection error: %p : %s",
345 event,
346 desc);
347 }
348
349 } else {
350 SC_log(LOG_ERR, "DNS configuration server: unknown event type : %p", type);
351
352 }
353 });
354
355 xpc_connection_resume(c);
356
357 SC_log(LOG_DEBUG, "XPC server \"%s\" started", name);
358
359 return;
360 }
361
362
363 __private_extern__
364 _Bool
365 _dns_configuration_store(dns_create_config_t *_config)
366 {
367 _dns_config_buf_t *config = (_dns_config_buf_t *)*_config;
368 Boolean in_sync;
369 uint64_t new_generation = 0;
370 CFDataRef new_dns_info = NULL;
371 const char *notify_key;
372
373 if (config != NULL) {
374 const UInt8 *bytes;
375 CFIndex len;
376
377 new_generation = config->config.generation;
378
379 SC_log(LOG_INFO, "DNS configuration updated: %llu",
380 new_generation);
381
382 bytes = (const UInt8 *)config;
383 len = sizeof(_dns_config_buf_t) + ntohl(config->n_attribute);
384
385 new_dns_info = CFDataCreate(NULL, bytes, len);
386 }
387
388 dispatch_sync(_dnsinfo_server_queue(), ^{
389 _libSC_info_server_set_data(&S_dns_info, new_dns_info, new_generation);
390 });
391
392 if (new_dns_info != NULL) {
393 CFRelease(new_dns_info);
394 }
395
396 // if anyone is keeping us in sync, they now need to catch up
397 in_sync = _libSC_info_server_in_sync(&S_dns_info);
398 if (S_sync_handler != NULL) {
399 S_sync_handler(in_sync);
400 }
401
402 // and let everyone else know that the configuration has been updated
403 notify_key = dns_configuration_notify_key();
404 if (notify_key != NULL) {
405 uint32_t status;
406
407 status = notify_post(notify_key);
408 if (status != NOTIFY_STATUS_OK) {
409 SC_log(LOG_ERR, "notify_post() failed: %d", status);
410 // notification posting failures are non-fatal
411 }
412 }
413
414 return TRUE;
415 }
416
417
418 #pragma mark -
419 #pragma mark Testing
420
421
422 #ifdef MAIN
423
424 int
425 main(int argc, char **argv)
426 {
427 static Boolean verbose = (argc > 1) ? TRUE : FALSE;
428 // _sc_log = FALSE;
429 _sc_verbose = (argc > 1) ? TRUE : FALSE;
430 _sc_debug = TRUE;
431
432 load_DNSConfiguration(CFBundleGetMainBundle(), // bundle
433 ^(Boolean inSync) { // sync handler
434 SC_log(LOG_INFO,
435 "in sync: %s",
436 inSync ? "Yes" : "No")
437 });
438 CFRunLoopRun();
439 /* not reached */
440 exit(0);
441 return 0;
442 }
443
444 #endif /* MAIN */