]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2012-2018, 2020 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 | * February 8, 2012 Allan Nathanson <ajn@apple.com> | |
28 | * - initial revision | |
29 | */ | |
30 | ||
31 | #include <notify.h> | |
32 | #include <dispatch/dispatch.h> | |
33 | #include <xpc/xpc.h> | |
34 | #include <os/state_private.h> | |
35 | #include <CommonCrypto/CommonDigest.h> | |
36 | #include <CoreFoundation/CoreFoundation.h> | |
37 | #include <SystemConfiguration/SCPrivate.h> | |
38 | ||
39 | #include "libSystemConfiguration_client.h" | |
40 | #include "libSystemConfiguration_server.h" | |
41 | ||
42 | #include <network_information.h> | |
43 | #include "network_information_internal.h" | |
44 | #include "network_information_server.h" | |
45 | #include "network_state_information_priv.h" | |
46 | ||
47 | #if !TARGET_OS_SIMULATOR | |
48 | #include "agent-monitor.h" | |
49 | #include "configAgentDefines.h" | |
50 | #include "network_config_agent_info_priv.h" | |
51 | #endif // !TARGET_OS_SIMULATOR | |
52 | ||
53 | #ifdef SC_LOG_HANDLE | |
54 | #include <os/log.h> | |
55 | os_log_t SC_LOG_HANDLE(void); | |
56 | #endif //SC_LOG_HANDLE | |
57 | ||
58 | ||
59 | #pragma mark - | |
60 | #pragma mark Globals | |
61 | ||
62 | ||
63 | /* | |
64 | * S_nwi_info | |
65 | * | |
66 | * Note: all accesses should be made while running on the _nwi_server_queue() | |
67 | */ | |
68 | static libSC_info_server_t S_nwi_info; | |
69 | ||
70 | ||
71 | /* | |
72 | * S_sync_handler | |
73 | * ACK (in-sync or not-in-sync) updates should be posted using | |
74 | * this handler | |
75 | * | |
76 | * Note: all accesses should be made while running on the _nwi_server_queue() | |
77 | */ | |
78 | static _nwi_sync_handler_t S_sync_handler = NULL; | |
79 | ||
80 | ||
81 | #pragma mark - | |
82 | #pragma mark Support functions | |
83 | ||
84 | ||
85 | #pragma mark - | |
86 | #pragma mark Network information server "main" | |
87 | ||
88 | ||
89 | static dispatch_queue_t | |
90 | _nwi_state_server_queue() | |
91 | { | |
92 | static dispatch_once_t once; | |
93 | static dispatch_queue_t q; | |
94 | ||
95 | dispatch_once(&once, ^{ | |
96 | q = dispatch_queue_create(NWI_SERVICE_NAME ".server", NULL); | |
97 | }); | |
98 | ||
99 | return q; | |
100 | } | |
101 | ||
102 | ||
103 | /* | |
104 | * _nwi_state_copy | |
105 | * | |
106 | * Called when a client wants a copy of the current | |
107 | * Network information | |
108 | * | |
109 | * - caller must be running on the _nwi_server_queue() | |
110 | */ | |
111 | static void | |
112 | _nwi_state_copy(xpc_connection_t connection, xpc_object_t request) | |
113 | { | |
114 | CFDataRef data; | |
115 | uint64_t generation; | |
116 | const char *proc_name; | |
117 | xpc_connection_t remote; | |
118 | xpc_object_t reply; | |
119 | ||
120 | remote = xpc_dictionary_get_remote_connection(request); | |
121 | reply = xpc_dictionary_create_reply(request); | |
122 | if (reply == NULL) { | |
123 | SC_log(LOG_ERR, "<%p> _nwi_state_copy: xpc_dictionary_create_reply: failed", | |
124 | connection); | |
125 | return; | |
126 | } | |
127 | ||
128 | // extract data and generation # | |
129 | data = _libSC_info_server_get_data(&S_nwi_info, connection, &generation); | |
130 | ||
131 | // extract process name | |
132 | proc_name = xpc_dictionary_get_string(request, NWI_PROC_NAME); | |
133 | if (proc_name == NULL) { | |
134 | proc_name = "???"; | |
135 | } | |
136 | ||
137 | SC_log(LOG_DEBUG, "<%p:%s[%d]> Network information copy: %llu", | |
138 | connection, | |
139 | proc_name, | |
140 | xpc_connection_get_pid(connection), | |
141 | generation); | |
142 | ||
143 | // return the Network information (if available) | |
144 | if (data != NULL) { | |
145 | xpc_dictionary_set_data(reply, | |
146 | NWI_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 | * _nwi_state_acknowledge | |
161 | * | |
162 | * Called when a client wants to acknowledge processing | |
163 | * of the Network information | |
164 | * | |
165 | * - caller must be running on the _nwi_server_queue() | |
166 | */ | |
167 | static void | |
168 | _nwi_state_acknowledge(xpc_connection_t connection, xpc_object_t request) | |
169 | { | |
170 | Boolean changed; | |
171 | uint64_t generation; | |
172 | ||
173 | generation = xpc_dictionary_get_uint64(request, NWI_GENERATION); | |
174 | ||
175 | SC_log(LOG_DEBUG, "<%p:%d> Network information ack: %llu", | |
176 | connection, | |
177 | xpc_connection_get_pid(connection), | |
178 | generation); | |
179 | ||
180 | changed = _libSC_info_server_acknowledged(&S_nwi_info, connection, generation); | |
181 | if (changed) { | |
182 | Boolean inSync; | |
183 | ||
184 | // report change | |
185 | inSync = _libSC_info_server_in_sync(&S_nwi_info); | |
186 | if (S_sync_handler != NULL) { | |
187 | S_sync_handler(inSync); | |
188 | } | |
189 | } | |
190 | ||
191 | return; | |
192 | } | |
193 | ||
194 | #if !TARGET_OS_SIMULATOR | |
195 | /* | |
196 | * _nwi_config_agent_copy | |
197 | * | |
198 | * Called when a client wants a copy of the agent data | |
199 | * | |
200 | * - caller must be running on the _nwi_server_queue() | |
201 | */ | |
202 | static void | |
203 | _nwi_config_agent_copy(xpc_connection_t connection, xpc_object_t request) | |
204 | { | |
205 | const void *buffer = NULL; | |
206 | const char *proc_name = NULL; | |
207 | uint64_t length = 0; | |
208 | xpc_connection_t remote; | |
209 | xpc_object_t reply = NULL; | |
210 | ||
211 | remote = xpc_dictionary_get_remote_connection(request); | |
212 | reply = xpc_dictionary_create_reply(request); | |
213 | ||
214 | uuid_t agent_uuid; | |
215 | const uint8_t *agent_uuid_value = xpc_dictionary_get_uuid(request, kConfigAgentAgentUUID); | |
216 | if (agent_uuid_value != NULL) { | |
217 | uuid_copy(agent_uuid, agent_uuid_value); | |
218 | } else { | |
219 | goto done; | |
220 | } | |
221 | ||
222 | const char *agent_type = xpc_dictionary_get_string(request, kConfigAgentType); | |
223 | if (agent_type == NULL) { | |
224 | goto done; | |
225 | } | |
226 | ||
227 | proc_name = xpc_dictionary_get_string(request, NWI_PROC_NAME); | |
228 | if (proc_name == NULL) { | |
229 | proc_name = "???"; | |
230 | } | |
231 | ||
232 | SC_log(LOG_DEBUG, "<%p:%s[%d]> Config agent information copy", | |
233 | connection, | |
234 | proc_name, | |
235 | xpc_connection_get_pid(connection)); | |
236 | ||
237 | if (strcmp(agent_type, kConfigAgentTypeDNS) == 0) { | |
238 | buffer = copy_dns_information_for_agent_uuid(agent_uuid, &length); | |
239 | } else if (strcmp(agent_type, kConfigAgentTypeProxy) == 0) { | |
240 | buffer = copy_proxy_information_for_agent_uuid(agent_uuid, &length); | |
241 | } | |
242 | ||
243 | if (buffer != NULL && length > 0) { | |
244 | xpc_dictionary_set_data(reply, | |
245 | kConfigAgentAgentData, | |
246 | buffer, | |
247 | (size_t)length); | |
248 | } | |
249 | ||
250 | xpc_connection_send_message(remote, reply); | |
251 | ||
252 | done: | |
253 | if (reply != NULL) { | |
254 | xpc_release(reply); | |
255 | } | |
256 | ||
257 | if (buffer != NULL) { | |
258 | free((void *)buffer); | |
259 | } | |
260 | ||
261 | return; | |
262 | } | |
263 | #endif // !TARGET_OS_SIMULATOR | |
264 | ||
265 | ||
266 | static void | |
267 | process_request(xpc_connection_t connection, xpc_object_t request) | |
268 | { | |
269 | int64_t op; | |
270 | ||
271 | op = xpc_dictionary_get_int64(request, NWI_REQUEST); | |
272 | switch (op) { | |
273 | case NWI_STATE_REQUEST_COPY : | |
274 | /* | |
275 | * Return the Network information | |
276 | */ | |
277 | _nwi_state_copy(connection, request); | |
278 | break; | |
279 | ||
280 | case NWI_STATE_REQUEST_ACKNOWLEDGE : | |
281 | /* | |
282 | * Acknowlege a [processed] Network information | |
283 | */ | |
284 | _nwi_state_acknowledge(connection, request); | |
285 | ||
286 | break; | |
287 | #if !TARGET_OS_SIMULATOR | |
288 | case NWI_CONFIG_AGENT_REQUEST_COPY : | |
289 | /* | |
290 | * Return the agent information | |
291 | */ | |
292 | _nwi_config_agent_copy(connection, request); | |
293 | ||
294 | break; | |
295 | #endif // !TARGET_OS_SIMULATOR | |
296 | default : | |
297 | SC_log(LOG_ERR, "<%p> unknown request : %lld", | |
298 | connection, | |
299 | op); | |
300 | ||
301 | break; | |
302 | } | |
303 | ||
304 | return; | |
305 | } | |
306 | ||
307 | ||
308 | static void | |
309 | process_new_connection(xpc_connection_t c) | |
310 | { | |
311 | SC_log(LOG_DEBUG, "<%p:%d> Network information session: open", | |
312 | c, | |
313 | xpc_connection_get_pid(c)); | |
314 | ||
315 | _libSC_info_server_open(&S_nwi_info, c); | |
316 | ||
317 | xpc_connection_set_target_queue(c, _nwi_state_server_queue()); | |
318 | ||
319 | xpc_connection_set_event_handler(c, ^(xpc_object_t xobj) { | |
320 | xpc_type_t type; | |
321 | ||
322 | type = xpc_get_type(xobj); | |
323 | if (type == XPC_TYPE_DICTIONARY) { | |
324 | // process the request | |
325 | process_request(c, xobj); | |
326 | ||
327 | } else if (type == XPC_TYPE_ERROR) { | |
328 | const char *desc; | |
329 | ||
330 | desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION); | |
331 | if (xobj == XPC_ERROR_CONNECTION_INVALID) { | |
332 | Boolean changed; | |
333 | ||
334 | SC_log(LOG_DEBUG, "<%p:%d> Network information session: close", | |
335 | c, | |
336 | xpc_connection_get_pid(c)); | |
337 | ||
338 | changed = _libSC_info_server_close(&S_nwi_info, c); | |
339 | if (changed) { | |
340 | Boolean inSync; | |
341 | ||
342 | // report change | |
343 | inSync = _libSC_info_server_in_sync(&S_nwi_info); | |
344 | if (S_sync_handler != NULL) { | |
345 | S_sync_handler(inSync); | |
346 | } | |
347 | } | |
348 | ||
349 | } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) { | |
350 | SC_log(LOG_ERR, "<%p:%d> %s", | |
351 | c, | |
352 | xpc_connection_get_pid(c), | |
353 | desc); | |
354 | ||
355 | } else { | |
356 | SC_log(LOG_ERR, "<%p:%d> Connection error: %p : %s", | |
357 | c, | |
358 | xpc_connection_get_pid(c), | |
359 | xobj, | |
360 | desc); | |
361 | } | |
362 | ||
363 | } else { | |
364 | SC_log(LOG_ERR, "<%p:%d> unknown event type : %p", | |
365 | c, | |
366 | xpc_connection_get_pid(c), | |
367 | type); | |
368 | } | |
369 | }); | |
370 | ||
371 | xpc_connection_resume(c); | |
372 | ||
373 | return; | |
374 | } | |
375 | ||
376 | ||
377 | #pragma mark - | |
378 | #pragma mark Network information state | |
379 | ||
380 | ||
381 | static void | |
382 | add_state_handler() | |
383 | { | |
384 | #if !TARGET_OS_SIMULATOR | |
385 | os_state_block_t state_block; | |
386 | os_state_handle_t state_handle; | |
387 | ||
388 | state_block = ^os_state_data_t(os_state_hints_t hints) { | |
389 | #pragma unused(hints) | |
390 | os_state_data_t state_data; | |
391 | size_t state_data_size; | |
392 | CFIndex state_len; | |
393 | ||
394 | state_len = (S_nwi_info.data != NULL) ? CFDataGetLength(S_nwi_info.data) : 0; | |
395 | state_data_size = OS_STATE_DATA_SIZE_NEEDED(state_len); | |
396 | if (state_data_size > MAX_STATEDUMP_SIZE) { | |
397 | SC_log(LOG_ERR, "Network information : state data too large (%zd > %zd)", | |
398 | state_data_size, | |
399 | (size_t)MAX_STATEDUMP_SIZE); | |
400 | return NULL; | |
401 | } | |
402 | ||
403 | state_data = calloc(1, state_data_size); | |
404 | if (state_data == NULL) { | |
405 | SC_log(LOG_ERR, "Network information: could not allocate state data"); | |
406 | return NULL; | |
407 | } | |
408 | ||
409 | state_data->osd_type = OS_STATE_DATA_CUSTOM; | |
410 | state_data->osd_data_size = (uint32_t)state_len; | |
411 | strlcpy(state_data->osd_decoder.osdd_library, | |
412 | "SystemConfiguration", | |
413 | sizeof(state_data->osd_decoder.osdd_library)); | |
414 | strlcpy(state_data->osd_decoder.osdd_type, | |
415 | "nwi", | |
416 | sizeof(state_data->osd_decoder.osdd_type)); | |
417 | strlcpy(state_data->osd_title, "Network information", sizeof(state_data->osd_title)); | |
418 | if (state_len > 0) { | |
419 | memcpy(state_data->osd_data, CFDataGetBytePtr(S_nwi_info.data), state_len); | |
420 | } | |
421 | ||
422 | return state_data; | |
423 | }; | |
424 | ||
425 | state_handle = os_state_add_handler(_nwi_state_server_queue(), state_block); | |
426 | if (state_handle == 0) { | |
427 | SC_log(LOG_ERR, "Network information: os_state_add_handler() failed"); | |
428 | } | |
429 | #endif // !TARGET_OS_SIMULATOR | |
430 | ||
431 | ||
432 | return; | |
433 | } | |
434 | ||
435 | ||
436 | #pragma mark - | |
437 | #pragma mark Network information server SPIs | |
438 | ||
439 | ||
440 | __private_extern__ | |
441 | void | |
442 | load_NetworkInformation(CFBundleRef bundle, | |
443 | _nwi_sync_handler_t syncHandler) | |
444 | { | |
445 | #pragma unused(bundle) | |
446 | xpc_connection_t c; | |
447 | const char *name; | |
448 | ||
449 | /* | |
450 | * keep track of Network information acknowledgements | |
451 | */ | |
452 | _libSC_info_server_init(&S_nwi_info); | |
453 | ||
454 | /* | |
455 | * add a state dump handler | |
456 | */ | |
457 | add_state_handler(); | |
458 | ||
459 | /* | |
460 | * save the in-sync/not-in-sync handler | |
461 | */ | |
462 | S_sync_handler = Block_copy(syncHandler); | |
463 | ||
464 | // create XPC listener | |
465 | name = getenv(NWI_SERVICE_NAME); | |
466 | if (name == NULL) { | |
467 | name = NWI_SERVICE_NAME; | |
468 | } | |
469 | ||
470 | c = xpc_connection_create_mach_service(name, | |
471 | _nwi_state_server_queue(), | |
472 | XPC_CONNECTION_MACH_SERVICE_LISTENER); | |
473 | ||
474 | xpc_connection_set_event_handler(c, ^(xpc_object_t event) { | |
475 | xpc_type_t type; | |
476 | ||
477 | type = xpc_get_type(event); | |
478 | if (type == XPC_TYPE_CONNECTION) { | |
479 | process_new_connection(event); | |
480 | ||
481 | } else if (type == XPC_TYPE_ERROR) { | |
482 | const char *desc; | |
483 | ||
484 | desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION); | |
485 | if (event == XPC_ERROR_CONNECTION_INVALID) { | |
486 | SC_log(LOG_ERR, "Network information server: %s", desc); | |
487 | xpc_release(c); | |
488 | } else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { | |
489 | SC_log(LOG_ERR, "Network information server: %s", desc); | |
490 | } else { | |
491 | SC_log(LOG_ERR, "Network information server: Connection error: %p : %s", | |
492 | event, | |
493 | desc); | |
494 | } | |
495 | ||
496 | } else { | |
497 | SC_log(LOG_ERR, "Network information server: unknown event type : %p", type); | |
498 | ||
499 | } | |
500 | }); | |
501 | ||
502 | xpc_connection_resume(c); | |
503 | ||
504 | SC_log(LOG_DEBUG, "XPC server \"%s\" started", name); | |
505 | ||
506 | return; | |
507 | } | |
508 | ||
509 | ||
510 | __private_extern__ | |
511 | _Bool | |
512 | _nwi_state_store(nwi_state *state) | |
513 | { | |
514 | Boolean in_sync; | |
515 | uint64_t new_generation = 0; | |
516 | CFDataRef new_nwi_info = NULL; | |
517 | const char *notify_key; | |
518 | ||
519 | if (state != NULL) { | |
520 | const UInt8 *bytes; | |
521 | CFIndex len; | |
522 | ||
523 | new_generation = state->generation_count; | |
524 | ||
525 | SC_log(LOG_DEBUG, "Network information updated: %llu", | |
526 | new_generation); | |
527 | ||
528 | bytes = (const UInt8 *)state; | |
529 | len = nwi_state_size(state); | |
530 | ||
531 | new_nwi_info = CFDataCreate(NULL, bytes, len); | |
532 | } | |
533 | ||
534 | dispatch_sync(_nwi_state_server_queue(), ^{ | |
535 | _libSC_info_server_set_data(&S_nwi_info, new_nwi_info, new_generation); | |
536 | }); | |
537 | ||
538 | if (new_nwi_info != NULL) { | |
539 | CFRelease(new_nwi_info); | |
540 | } | |
541 | ||
542 | // if anyone is keeping us in sync, they now need to catchup | |
543 | in_sync = _libSC_info_server_in_sync(&S_nwi_info); | |
544 | if (S_sync_handler != NULL) { | |
545 | S_sync_handler(in_sync); | |
546 | } | |
547 | ||
548 | // and let everyone else know that the configuration has been updated | |
549 | notify_key = nwi_state_get_notify_key(); | |
550 | if (notify_key != NULL) { | |
551 | uint32_t status; | |
552 | ||
553 | _nwi_state_force_refresh(); | |
554 | status = notify_post(notify_key); | |
555 | if (status != NOTIFY_STATUS_OK) { | |
556 | SC_log(LOG_ERR, "notify_post() failed: %d", status); | |
557 | // notification posting failures are non-fatal | |
558 | } | |
559 | } | |
560 | ||
561 | return TRUE; | |
562 | } | |
563 | ||
564 | ||
565 | #pragma mark - | |
566 | #pragma mark Testing | |
567 | ||
568 | ||
569 | #ifdef MAIN | |
570 | ||
571 | int | |
572 | main(int argc, char **argv) | |
573 | { | |
574 | static Boolean verbose = (argc > 1) ? TRUE : FALSE; | |
575 | _sc_log = kSCLogDestinationFile; | |
576 | _sc_verbose = (argc > 1) ? TRUE : FALSE; | |
577 | _sc_debug = TRUE; | |
578 | ||
579 | load_NetworkInformation(CFBundleGetMainBundle(), // bundle | |
580 | ^(Boolean inSync) { // sync handler | |
581 | SC_log(LOG_INFO, | |
582 | "in sync: %s", | |
583 | inSync ? "Yes" : "No") | |
584 | }); | |
585 | CFRunLoopRun(); | |
586 | /* not reached */ | |
587 | exit(0); | |
588 | return 0; | |
589 | } | |
590 | ||
591 | #endif /* MAIN */ |