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