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