]> git.saurik.com Git - apple/configd.git/blob - dnsinfo/dnsinfo_server.c
configd-963.50.8.tar.gz
[apple/configd.git] / dnsinfo / dnsinfo_server.c
1 /*
2 * Copyright (c) 2004-2008, 2011-2017 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 <os/state_private.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <SystemConfiguration/SCPrivate.h>
44
45 #include "libSystemConfiguration_client.h"
46 #include "libSystemConfiguration_server.h"
47
48 #include "dnsinfo_create.h"
49 #include "dnsinfo_server.h"
50 #include "dnsinfo_private.h"
51
52 #ifdef SC_LOG_HANDLE
53 #include <os/log.h>
54 os_log_t SC_LOG_HANDLE;
55 #endif //SC_LOG_HANDLE
56
57
58 #pragma mark -
59 #pragma mark Globals
60
61
62 /*
63 * S_dns_info
64 *
65 * Note: all accesses should be made while running on the _dns_server_queue()
66 */
67 static libSC_info_server_t S_dns_info;
68
69
70 /*
71 * S_sync_handler
72 * ACK (in-sync or not-in-sync) updates should be posted using
73 * this handler
74 *
75 * Note: all accesses should be made while running on the _dns_server_queue()
76 */
77 static _dns_sync_handler_t S_sync_handler = NULL;
78
79
80 #pragma mark -
81 #pragma mark Support functions
82
83
84 #pragma mark -
85 #pragma mark DNS configuration server
86
87
88 static dispatch_queue_t
89 _dnsinfo_server_queue()
90 {
91 static dispatch_once_t once;
92 static dispatch_queue_t q;
93
94 dispatch_once(&once, ^{
95 q = dispatch_queue_create(DNSINFO_SERVICE_NAME ".server", NULL);
96 });
97
98 return q;
99 }
100
101
102 /*
103 * _dnsinfo_copy
104 *
105 * Called when a client wants a copy of the current
106 * DNS configuration
107 *
108 * - caller must be running on the _dns_server_queue()
109 */
110 static void
111 _dnsinfo_copy(xpc_connection_t connection, xpc_object_t request)
112 {
113 CFDataRef data;
114 uint64_t generation;
115 const char *proc_name;
116 xpc_connection_t remote;
117 xpc_object_t reply;
118
119 remote = xpc_dictionary_get_remote_connection(request);
120 reply = xpc_dictionary_create_reply(request);
121 if (reply == NULL) {
122 SC_log(LOG_ERR, "<%p> _dnsinfo_copy: xpc_dictionary_create_reply: failed",
123 connection);
124 return;
125 }
126
127 // extract data and generation #
128 data = _libSC_info_server_get_data(&S_dns_info,
129 connection,
130 &generation);
131
132 // extract process name
133 proc_name = xpc_dictionary_get_string(request, DNSINFO_PROC_NAME);
134 if (proc_name == NULL) {
135 proc_name = "???";
136 }
137
138 SC_log(LOG_DEBUG, "<%p:%s[%d]> DNS configuration copy: %llu",
139 connection,
140 proc_name,
141 xpc_connection_get_pid(connection),
142 generation);
143
144 // return the DNS configuration (if available)
145 if (data != NULL) {
146 xpc_dictionary_set_data(reply,
147 DNSINFO_CONFIGURATION,
148 CFDataGetBytePtr(data),
149 CFDataGetLength(data));
150 }
151
152 // reply
153 xpc_connection_send_message(remote, reply);
154 xpc_release(reply);
155
156 return;
157 }
158
159
160 /*
161 * _dnsinfo_acknowledge
162 *
163 * Called when a client wants to acknowledge processing
164 * of the DNS configuration
165 *
166 * - caller must be running on the _dns_server_queue()
167 */
168 static void
169 _dnsinfo_acknowledge(xpc_connection_t connection, xpc_object_t request)
170 {
171 uint64_t generation;
172 Boolean inSync;
173
174 generation = xpc_dictionary_get_uint64(request, DNSINFO_GENERATION);
175
176 SC_log(LOG_DEBUG, "<%p:%d> DNS configuration ack: %llu",
177 connection,
178 xpc_connection_get_pid(connection),
179 generation);
180
181 (void) _libSC_info_server_acknowledged(&S_dns_info, connection, generation);
182
183 // Note: all of the DNS service ack's should result
184 // in a [new] network change being posted
185
186 inSync = _libSC_info_server_in_sync(&S_dns_info);
187 if (S_sync_handler != NULL) {
188 S_sync_handler(inSync);
189 }
190 return;
191 }
192
193
194 static void
195 process_request(xpc_connection_t connection, xpc_object_t request)
196 {
197 int64_t op;
198
199 op = xpc_dictionary_get_int64(request, DNSINFO_REQUEST);
200 switch (op) {
201 case DNSINFO_REQUEST_COPY :
202 /*
203 * Return the DNS configuration
204 */
205 _dnsinfo_copy(connection, request);
206 break;
207
208 case DNSINFO_REQUEST_ACKNOWLEDGE :
209 /*
210 * Acknowlege a [processed] DNS configuration
211 */
212 _dnsinfo_acknowledge(connection, request);
213
214 break;
215 default :
216 SC_log(LOG_ERR, "<%p> unknown request : %lld",
217 connection,
218 op);
219
220 break;
221 }
222
223 return;
224 }
225
226
227 static void
228 process_new_connection(xpc_connection_t c)
229 {
230 SC_log(LOG_DEBUG, "<%p:%d> DNS configuration session: open",
231 c,
232 xpc_connection_get_pid(c));
233
234 _libSC_info_server_open(&S_dns_info, c);
235
236 xpc_connection_set_target_queue(c, _dnsinfo_server_queue());
237
238 xpc_connection_set_event_handler(c, ^(xpc_object_t xobj) {
239 xpc_type_t type;
240
241 type = xpc_get_type(xobj);
242 if (type == XPC_TYPE_DICTIONARY) {
243 // process the request
244 process_request(c, xobj);
245
246 } else if (type == XPC_TYPE_ERROR) {
247 const char *desc;
248
249 desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
250 if (xobj == XPC_ERROR_CONNECTION_INVALID) {
251 Boolean changed;
252
253 SC_log(LOG_DEBUG, "<%p:%d> DNS configuration session: close",
254 c,
255 xpc_connection_get_pid(c));
256
257 changed = _libSC_info_server_close(&S_dns_info, c);
258 if (changed) {
259 Boolean inSync;
260
261 // report change
262 inSync = _libSC_info_server_in_sync(&S_dns_info);
263 if (S_sync_handler != NULL) {
264 S_sync_handler(inSync);
265 }
266 }
267
268 } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) {
269 SC_log(LOG_ERR, "<%p:%d> %s",
270 c,
271 xpc_connection_get_pid(c),
272 desc);
273
274 } else {
275 SC_log(LOG_ERR, "<%p:%d> Connection error: %p : %s",
276 c,
277 xpc_connection_get_pid(c),
278 xobj,
279 desc);
280 }
281
282 } else {
283 SC_log(LOG_ERR, "<%p:%d> unknown event type : %p",
284 c,
285 xpc_connection_get_pid(c),
286 type);
287 }
288 });
289
290 xpc_connection_resume(c);
291
292 return;
293 }
294
295
296 #pragma mark -
297 #pragma mark DNS configuration state
298
299
300 static void
301 add_state_handler()
302 {
303 #if !TARGET_OS_SIMULATOR
304 os_state_block_t state_block;
305 os_state_handle_t state_handle;
306
307 state_block = ^os_state_data_t(os_state_hints_t hints) {
308 #pragma unused(hints)
309 CFIndex dnsinfo_len;
310 os_state_data_t state_data;
311 size_t state_data_size;
312
313 dnsinfo_len = (S_dns_info.data != NULL) ? CFDataGetLength(S_dns_info.data) : 0;
314 state_data_size = OS_STATE_DATA_SIZE_NEEDED(dnsinfo_len);
315 if (state_data_size > MAX_STATEDUMP_SIZE) {
316 SC_log(LOG_ERR, "DNS configuration: state data too large (%zd > %zd)",
317 state_data_size,
318 (size_t)MAX_STATEDUMP_SIZE);
319 return NULL;
320 }
321
322 state_data = calloc(1, state_data_size);
323 if (state_data == NULL) {
324 SC_log(LOG_ERR, "DNS configuration: could not allocate state data");
325 return NULL;
326 }
327
328 state_data->osd_type = OS_STATE_DATA_CUSTOM;
329 state_data->osd_data_size = (uint32_t)dnsinfo_len;
330 strlcpy(state_data->osd_decoder.osdd_library,
331 "SystemConfiguration",
332 sizeof(state_data->osd_decoder.osdd_library));
333 strlcpy(state_data->osd_decoder.osdd_type,
334 "dnsinfo",
335 sizeof(state_data->osd_decoder.osdd_type));
336 strlcpy(state_data->osd_title, "DNS Configuration", sizeof(state_data->osd_title));
337 if (dnsinfo_len > 0) {
338 memcpy(state_data->osd_data, CFDataGetBytePtr(S_dns_info.data), dnsinfo_len);
339 }
340
341 return state_data;
342 };
343
344 state_handle = os_state_add_handler(_dnsinfo_server_queue(), state_block);
345 if (state_handle == 0) {
346 SC_log(LOG_ERR, "DNS configuration: os_state_add_handler() failed");
347 }
348 #endif // !TARGET_OS_SIMULATOR
349
350
351 return;
352 }
353
354
355 #pragma mark -
356 #pragma mark DNS configuration server SPIs
357
358
359 __private_extern__
360 void
361 load_DNSConfiguration(CFBundleRef bundle,
362 _dns_sync_handler_t syncHandler)
363 {
364 #pragma unused(bundle)
365 xpc_connection_t c;
366 const char *name;
367
368 /*
369 * keep track of DNS configuration acknowledgements
370 */
371 _libSC_info_server_init(&S_dns_info);
372
373 /*
374 * add a state dump handler
375 */
376 add_state_handler();
377
378 /*
379 * save the in-sync/not-in-sync handler
380 */
381 S_sync_handler = Block_copy(syncHandler);
382
383 // create XPC listener
384 name = getenv(DNSINFO_SERVICE_NAME);
385 if (name == NULL) {
386 name = DNSINFO_SERVICE_NAME;
387 }
388
389 c = xpc_connection_create_mach_service(name,
390 _dnsinfo_server_queue(),
391 XPC_CONNECTION_MACH_SERVICE_LISTENER);
392
393 xpc_connection_set_event_handler(c, ^(xpc_object_t event) {
394 xpc_type_t type;
395
396 type = xpc_get_type(event);
397 if (type == XPC_TYPE_CONNECTION) {
398 process_new_connection(event);
399
400 } else if (type == XPC_TYPE_ERROR) {
401 const char *desc;
402
403 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
404 if (event == XPC_ERROR_CONNECTION_INVALID) {
405 SC_log(LOG_ERR, "DNS configuration server: %s", desc);
406 xpc_release(c);
407 } else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
408 SC_log(LOG_ERR, "DNS configuration server: %s", desc);
409 } else {
410 SC_log(LOG_ERR, "DNS configuration server: Connection error: %p : %s",
411 event,
412 desc);
413 }
414
415 } else {
416 SC_log(LOG_ERR, "DNS configuration server: unknown event type : %p", type);
417 }
418 });
419
420 xpc_connection_resume(c);
421
422 SC_log(LOG_DEBUG, "XPC server \"%s\" started", name);
423
424 return;
425 }
426
427
428 __private_extern__
429 _Bool
430 _dns_configuration_store(dns_create_config_t *_config)
431 {
432 _dns_config_buf_t *config = (_dns_config_buf_t *)*_config;
433 Boolean in_sync;
434 uint64_t new_generation = 0;
435 CFDataRef new_dns_info = NULL;
436 const char *notify_key;
437
438 if (config != NULL) {
439 const UInt8 *bytes;
440 CFIndex len;
441
442 new_generation = config->config.generation;
443
444 SC_log(LOG_INFO, "DNS configuration updated: %llu",
445 new_generation);
446
447 bytes = (const UInt8 *)config;
448 len = sizeof(_dns_config_buf_t) + ntohl(config->n_attribute);
449
450 new_dns_info = CFDataCreate(NULL, bytes, len);
451 }
452
453 dispatch_sync(_dnsinfo_server_queue(), ^{
454 _libSC_info_server_set_data(&S_dns_info, new_dns_info, new_generation);
455 });
456
457 if (new_dns_info != NULL) {
458 CFRelease(new_dns_info);
459 }
460
461 // if anyone is keeping us in sync, they now need to catch up
462 in_sync = _libSC_info_server_in_sync(&S_dns_info);
463 if (S_sync_handler != NULL) {
464 S_sync_handler(in_sync);
465 }
466
467 // and let everyone else know that the configuration has been updated
468 notify_key = dns_configuration_notify_key();
469 if (notify_key != NULL) {
470 uint32_t status;
471
472 status = notify_post(notify_key);
473 if (status != NOTIFY_STATUS_OK) {
474 SC_log(LOG_ERR, "notify_post() failed: %d", status);
475 // notification posting failures are non-fatal
476 }
477 }
478
479 return TRUE;
480 }
481
482
483 #pragma mark -
484 #pragma mark Testing
485
486
487 #ifdef MAIN
488
489 int
490 main(int argc, char **argv)
491 {
492 static Boolean verbose = (argc > 1) ? TRUE : FALSE;
493 // _sc_log = FALSE;
494 _sc_verbose = (argc > 1) ? TRUE : FALSE;
495 _sc_debug = TRUE;
496
497 load_DNSConfiguration(CFBundleGetMainBundle(), // bundle
498 ^(Boolean inSync) { // sync handler
499 SC_log(LOG_INFO,
500 "in sync: %s",
501 inSync ? "Yes" : "No")
502 });
503 CFRunLoopRun();
504 /* not reached */
505 exit(0);
506 return 0;
507 }
508
509 #endif /* MAIN */