2 * Copyright (c) 2012-2018, 2020 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * February 8, 2012 Allan Nathanson <ajn@apple.com>
32 #include <dispatch/dispatch.h>
34 #include <os/state_private.h>
35 #include <CommonCrypto/CommonDigest.h>
36 #include <CoreFoundation/CoreFoundation.h>
37 #include <SystemConfiguration/SCPrivate.h>
39 #include "libSystemConfiguration_client.h"
40 #include "libSystemConfiguration_server.h"
42 #include <network_information.h>
43 #include "network_information_internal.h"
44 #include "network_information_server.h"
45 #include "network_state_information_priv.h"
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
55 os_log_t
SC_LOG_HANDLE(void);
56 #endif //SC_LOG_HANDLE
66 * Note: all accesses should be made while running on the _nwi_server_queue()
68 static libSC_info_server_t S_nwi_info
;
73 * ACK (in-sync or not-in-sync) updates should be posted using
76 * Note: all accesses should be made while running on the _nwi_server_queue()
78 static _nwi_sync_handler_t S_sync_handler
= NULL
;
82 #pragma mark Support functions
86 #pragma mark Network information server "main"
89 static dispatch_queue_t
90 _nwi_state_server_queue()
92 static dispatch_once_t once
;
93 static dispatch_queue_t q
;
95 dispatch_once(&once
, ^{
96 q
= dispatch_queue_create(NWI_SERVICE_NAME
".server", NULL
);
106 * Called when a client wants a copy of the current
107 * Network information
109 * - caller must be running on the _nwi_server_queue()
112 _nwi_state_copy(xpc_connection_t connection
, xpc_object_t request
)
116 const char *proc_name
;
117 xpc_connection_t remote
;
120 remote
= xpc_dictionary_get_remote_connection(request
);
121 reply
= xpc_dictionary_create_reply(request
);
123 SC_log(LOG_ERR
, "<%p> _nwi_state_copy: xpc_dictionary_create_reply: failed",
128 // extract data and generation #
129 data
= _libSC_info_server_get_data(&S_nwi_info
, connection
, &generation
);
131 // extract process name
132 proc_name
= xpc_dictionary_get_string(request
, NWI_PROC_NAME
);
133 if (proc_name
== NULL
) {
137 SC_log(LOG_DEBUG
, "<%p:%s[%d]> Network information copy: %llu",
140 xpc_connection_get_pid(connection
),
143 // return the Network information (if available)
145 xpc_dictionary_set_data(reply
,
147 CFDataGetBytePtr(data
),
148 CFDataGetLength(data
));
152 xpc_connection_send_message(remote
, reply
);
160 * _nwi_state_acknowledge
162 * Called when a client wants to acknowledge processing
163 * of the Network information
165 * - caller must be running on the _nwi_server_queue()
168 _nwi_state_acknowledge(xpc_connection_t connection
, xpc_object_t request
)
173 generation
= xpc_dictionary_get_uint64(request
, NWI_GENERATION
);
175 SC_log(LOG_DEBUG
, "<%p:%d> Network information ack: %llu",
177 xpc_connection_get_pid(connection
),
180 changed
= _libSC_info_server_acknowledged(&S_nwi_info
, connection
, generation
);
185 inSync
= _libSC_info_server_in_sync(&S_nwi_info
);
186 if (S_sync_handler
!= NULL
) {
187 S_sync_handler(inSync
);
194 #if !TARGET_OS_SIMULATOR
196 * _nwi_config_agent_copy
198 * Called when a client wants a copy of the agent data
200 * - caller must be running on the _nwi_server_queue()
203 _nwi_config_agent_copy(xpc_connection_t connection
, xpc_object_t request
)
205 const void *buffer
= NULL
;
206 const char *proc_name
= NULL
;
208 xpc_connection_t remote
;
209 xpc_object_t reply
= NULL
;
211 remote
= xpc_dictionary_get_remote_connection(request
);
212 reply
= xpc_dictionary_create_reply(request
);
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
);
222 const char *agent_type
= xpc_dictionary_get_string(request
, kConfigAgentType
);
223 if (agent_type
== NULL
) {
227 proc_name
= xpc_dictionary_get_string(request
, NWI_PROC_NAME
);
228 if (proc_name
== NULL
) {
232 SC_log(LOG_DEBUG
, "<%p:%s[%d]> Config agent information copy",
235 xpc_connection_get_pid(connection
));
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
);
243 if (buffer
!= NULL
&& length
> 0) {
244 xpc_dictionary_set_data(reply
,
245 kConfigAgentAgentData
,
250 xpc_connection_send_message(remote
, reply
);
257 if (buffer
!= NULL
) {
258 free((void *)buffer
);
263 #endif // !TARGET_OS_SIMULATOR
267 process_request(xpc_connection_t connection
, xpc_object_t request
)
271 op
= xpc_dictionary_get_int64(request
, NWI_REQUEST
);
273 case NWI_STATE_REQUEST_COPY
:
275 * Return the Network information
277 _nwi_state_copy(connection
, request
);
280 case NWI_STATE_REQUEST_ACKNOWLEDGE
:
282 * Acknowlege a [processed] Network information
284 _nwi_state_acknowledge(connection
, request
);
287 #if !TARGET_OS_SIMULATOR
288 case NWI_CONFIG_AGENT_REQUEST_COPY
:
290 * Return the agent information
292 _nwi_config_agent_copy(connection
, request
);
295 #endif // !TARGET_OS_SIMULATOR
297 SC_log(LOG_ERR
, "<%p> unknown request : %lld",
309 process_new_connection(xpc_connection_t c
)
311 SC_log(LOG_DEBUG
, "<%p:%d> Network information session: open",
313 xpc_connection_get_pid(c
));
315 _libSC_info_server_open(&S_nwi_info
, c
);
317 xpc_connection_set_target_queue(c
, _nwi_state_server_queue());
319 xpc_connection_set_event_handler(c
, ^(xpc_object_t xobj
) {
322 type
= xpc_get_type(xobj
);
323 if (type
== XPC_TYPE_DICTIONARY
) {
324 // process the request
325 process_request(c
, xobj
);
327 } else if (type
== XPC_TYPE_ERROR
) {
330 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
331 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
334 SC_log(LOG_DEBUG
, "<%p:%d> Network information session: close",
336 xpc_connection_get_pid(c
));
338 changed
= _libSC_info_server_close(&S_nwi_info
, c
);
343 inSync
= _libSC_info_server_in_sync(&S_nwi_info
);
344 if (S_sync_handler
!= NULL
) {
345 S_sync_handler(inSync
);
349 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
350 SC_log(LOG_ERR
, "<%p:%d> %s",
352 xpc_connection_get_pid(c
),
356 SC_log(LOG_ERR
, "<%p:%d> Connection error: %p : %s",
358 xpc_connection_get_pid(c
),
364 SC_log(LOG_ERR
, "<%p:%d> unknown event type : %p",
366 xpc_connection_get_pid(c
),
371 xpc_connection_resume(c
);
378 #pragma mark Network information state
384 #if !TARGET_OS_SIMULATOR
385 os_state_block_t state_block
;
386 os_state_handle_t state_handle
;
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
;
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)",
399 (size_t)MAX_STATEDUMP_SIZE
);
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");
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
,
416 sizeof(state_data
->osd_decoder
.osdd_type
));
417 strlcpy(state_data
->osd_title
, "Network information", sizeof(state_data
->osd_title
));
419 memcpy(state_data
->osd_data
, CFDataGetBytePtr(S_nwi_info
.data
), state_len
);
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");
429 #endif // !TARGET_OS_SIMULATOR
437 #pragma mark Network information server SPIs
442 load_NetworkInformation(CFBundleRef bundle
,
443 _nwi_sync_handler_t syncHandler
)
445 #pragma unused(bundle)
450 * keep track of Network information acknowledgements
452 _libSC_info_server_init(&S_nwi_info
);
455 * add a state dump handler
460 * save the in-sync/not-in-sync handler
462 S_sync_handler
= Block_copy(syncHandler
);
464 // create XPC listener
465 name
= getenv(NWI_SERVICE_NAME
);
467 name
= NWI_SERVICE_NAME
;
470 c
= xpc_connection_create_mach_service(name
,
471 _nwi_state_server_queue(),
472 XPC_CONNECTION_MACH_SERVICE_LISTENER
);
474 xpc_connection_set_event_handler(c
, ^(xpc_object_t event
) {
477 type
= xpc_get_type(event
);
478 if (type
== XPC_TYPE_CONNECTION
) {
479 process_new_connection(event
);
481 } else if (type
== XPC_TYPE_ERROR
) {
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
);
488 } else if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
489 SC_log(LOG_ERR
, "Network information server: %s", desc
);
491 SC_log(LOG_ERR
, "Network information server: Connection error: %p : %s",
497 SC_log(LOG_ERR
, "Network information server: unknown event type : %p", type
);
502 xpc_connection_resume(c
);
504 SC_log(LOG_DEBUG
, "XPC server \"%s\" started", name
);
512 _nwi_state_store(nwi_state
*state
)
515 uint64_t new_generation
= 0;
516 CFDataRef new_nwi_info
= NULL
;
517 const char *notify_key
;
523 new_generation
= state
->generation_count
;
525 SC_log(LOG_DEBUG
, "Network information updated: %llu",
528 bytes
= (const UInt8
*)state
;
529 len
= nwi_state_size(state
);
531 new_nwi_info
= CFDataCreate(NULL
, bytes
, len
);
534 dispatch_sync(_nwi_state_server_queue(), ^{
535 _libSC_info_server_set_data(&S_nwi_info
, new_nwi_info
, new_generation
);
538 if (new_nwi_info
!= NULL
) {
539 CFRelease(new_nwi_info
);
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
);
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
) {
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
572 main(int argc
, char **argv
)
574 static Boolean verbose
= (argc
> 1) ? TRUE
: FALSE
;
575 _sc_log
= kSCLogDestinationFile
;
576 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
579 load_NetworkInformation(CFBundleGetMainBundle(), // bundle
580 ^(Boolean inSync
) { // sync handler
583 inSync
? "Yes" : "No")