]> git.saurik.com Git - apple/configd.git/blob - nwi/network_information_server.c
configd-963.200.27.tar.gz
[apple/configd.git] / nwi / network_information_server.c
1 /*
2 * Copyright (c) 2012-2018 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;
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 = FALSE;
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 */