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