2 * Copyright (c) 2012-2017 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_state_information_priv.h"
44 #include "network_information_server.h"
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
54 os_log_t SC_LOG_HANDLE
;
55 #endif //SC_LOG_HANDLE
65 * Note: all accesses should be made while running on the _nwi_server_queue()
67 static libSC_info_server_t S_nwi_info
;
72 * ACK (in-sync or not-in-sync) updates should be posted using
75 * Note: all accesses should be made while running on the _nwi_server_queue()
77 static _nwi_sync_handler_t S_sync_handler
= NULL
;
81 #pragma mark Support functions
85 #pragma mark Network information server "main"
88 static dispatch_queue_t
89 _nwi_state_server_queue()
91 static dispatch_once_t once
;
92 static dispatch_queue_t q
;
94 dispatch_once(&once
, ^{
95 q
= dispatch_queue_create(NWI_SERVICE_NAME
".server", NULL
);
105 * Called when a client wants a copy of the current
106 * Network information
108 * - caller must be running on the _nwi_server_queue()
111 _nwi_state_copy(xpc_connection_t connection
, xpc_object_t request
)
115 const char *proc_name
;
116 xpc_connection_t remote
;
119 remote
= xpc_dictionary_get_remote_connection(request
);
120 reply
= xpc_dictionary_create_reply(request
);
122 SC_log(LOG_ERR
, "<%p> _nwi_state_copy: xpc_dictionary_create_reply: failed",
127 // extract data and generation #
128 data
= _libSC_info_server_get_data(&S_nwi_info
, connection
, &generation
);
130 // extract process name
131 proc_name
= xpc_dictionary_get_string(request
, NWI_PROC_NAME
);
132 if (proc_name
== NULL
) {
136 SC_log(LOG_DEBUG
, "<%p:%s[%d]> Network information copy: %llu",
139 xpc_connection_get_pid(connection
),
142 // return the Network information (if available)
144 xpc_dictionary_set_data(reply
,
146 CFDataGetBytePtr(data
),
147 CFDataGetLength(data
));
151 xpc_connection_send_message(remote
, reply
);
159 * _nwi_state_acknowledge
161 * Called when a client wants to acknowledge processing
162 * of the Network information
164 * - caller must be running on the _nwi_server_queue()
167 _nwi_state_acknowledge(xpc_connection_t connection
, xpc_object_t request
)
172 generation
= xpc_dictionary_get_uint64(request
, NWI_GENERATION
);
174 SC_log(LOG_DEBUG
, "<%p:%d> Network information ack: %llu",
176 xpc_connection_get_pid(connection
),
179 changed
= _libSC_info_server_acknowledged(&S_nwi_info
, connection
, generation
);
184 inSync
= _libSC_info_server_in_sync(&S_nwi_info
);
185 if (S_sync_handler
!= NULL
) {
186 S_sync_handler(inSync
);
193 #if !TARGET_OS_SIMULATOR
195 * _nwi_config_agent_copy
197 * Called when a client wants a copy of the agent data
199 * - caller must be running on the _nwi_server_queue()
202 _nwi_config_agent_copy(xpc_connection_t connection
, xpc_object_t request
)
204 const void *buffer
= NULL
;
205 const char *proc_name
= NULL
;
207 xpc_connection_t remote
;
208 xpc_object_t reply
= NULL
;
210 remote
= xpc_dictionary_get_remote_connection(request
);
211 reply
= xpc_dictionary_create_reply(request
);
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
);
221 const char *agent_type
= xpc_dictionary_get_string(request
, kConfigAgentType
);
222 if (agent_type
== NULL
) {
226 proc_name
= xpc_dictionary_get_string(request
, NWI_PROC_NAME
);
227 if (proc_name
== NULL
) {
231 SC_log(LOG_DEBUG
, "<%p:%s[%d]> Config agent information copy",
234 xpc_connection_get_pid(connection
));
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
);
242 if (buffer
!= NULL
&& length
> 0) {
243 xpc_dictionary_set_data(reply
,
244 kConfigAgentAgentData
,
249 xpc_connection_send_message(remote
, reply
);
256 if (buffer
!= NULL
) {
257 free((void *)buffer
);
262 #endif // !TARGET_OS_SIMULATOR
266 process_request(xpc_connection_t connection
, xpc_object_t request
)
270 op
= xpc_dictionary_get_int64(request
, NWI_REQUEST
);
272 case NWI_STATE_REQUEST_COPY
:
274 * Return the Network information
276 _nwi_state_copy(connection
, request
);
279 case NWI_STATE_REQUEST_ACKNOWLEDGE
:
281 * Acknowlege a [processed] Network information
283 _nwi_state_acknowledge(connection
, request
);
286 #if !TARGET_OS_SIMULATOR
287 case NWI_CONFIG_AGENT_REQUEST_COPY
:
289 * Return the agent information
291 _nwi_config_agent_copy(connection
, request
);
294 #endif // !TARGET_OS_SIMULATOR
296 SC_log(LOG_ERR
, "<%p> unknown request : %lld",
308 process_new_connection(xpc_connection_t c
)
310 SC_log(LOG_DEBUG
, "<%p:%d> Network information session: open",
312 xpc_connection_get_pid(c
));
314 _libSC_info_server_open(&S_nwi_info
, c
);
316 xpc_connection_set_target_queue(c
, _nwi_state_server_queue());
318 xpc_connection_set_event_handler(c
, ^(xpc_object_t xobj
) {
321 type
= xpc_get_type(xobj
);
322 if (type
== XPC_TYPE_DICTIONARY
) {
323 // process the request
324 process_request(c
, xobj
);
326 } else if (type
== XPC_TYPE_ERROR
) {
329 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
330 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
333 SC_log(LOG_DEBUG
, "<%p:%d> Network information session: close",
335 xpc_connection_get_pid(c
));
337 changed
= _libSC_info_server_close(&S_nwi_info
, c
);
342 inSync
= _libSC_info_server_in_sync(&S_nwi_info
);
343 if (S_sync_handler
!= NULL
) {
344 S_sync_handler(inSync
);
348 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
349 SC_log(LOG_ERR
, "<%p:%d> %s",
351 xpc_connection_get_pid(c
),
355 SC_log(LOG_ERR
, "<%p:%d> Connection error: %p : %s",
357 xpc_connection_get_pid(c
),
363 SC_log(LOG_ERR
, "<%p:%d> unknown event type : %p",
365 xpc_connection_get_pid(c
),
370 xpc_connection_resume(c
);
377 #pragma mark Network information state
383 #if !TARGET_OS_SIMULATOR
384 os_state_block_t state_block
;
385 os_state_handle_t state_handle
;
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
;
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)",
398 (size_t)MAX_STATEDUMP_SIZE
);
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");
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
,
415 sizeof(state_data
->osd_decoder
.osdd_type
));
416 strlcpy(state_data
->osd_title
, "Network information", sizeof(state_data
->osd_title
));
418 memcpy(state_data
->osd_data
, CFDataGetBytePtr(S_nwi_info
.data
), state_len
);
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");
428 #endif // !TARGET_OS_SIMULATOR
436 #pragma mark Network information server SPIs
441 load_NetworkInformation(CFBundleRef bundle
,
442 _nwi_sync_handler_t syncHandler
)
444 #pragma unused(bundle)
449 * keep track of Network information acknowledgements
451 _libSC_info_server_init(&S_nwi_info
);
454 * add a state dump handler
459 * save the in-sync/not-in-sync handler
461 S_sync_handler
= Block_copy(syncHandler
);
463 // create XPC listener
464 name
= getenv(NWI_SERVICE_NAME
);
466 name
= NWI_SERVICE_NAME
;
469 c
= xpc_connection_create_mach_service(name
,
470 _nwi_state_server_queue(),
471 XPC_CONNECTION_MACH_SERVICE_LISTENER
);
473 xpc_connection_set_event_handler(c
, ^(xpc_object_t event
) {
476 type
= xpc_get_type(event
);
477 if (type
== XPC_TYPE_CONNECTION
) {
478 process_new_connection(event
);
480 } else if (type
== XPC_TYPE_ERROR
) {
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
);
487 } else if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
488 SC_log(LOG_ERR
, "Network information server: %s", desc
);
490 SC_log(LOG_ERR
, "Network information server: Connection error: %p : %s",
496 SC_log(LOG_ERR
, "Network information server: unknown event type : %p", type
);
501 xpc_connection_resume(c
);
503 SC_log(LOG_DEBUG
, "XPC server \"%s\" started", name
);
511 _nwi_state_store(nwi_state
*state
)
514 uint64_t new_generation
= 0;
515 CFDataRef new_nwi_info
= NULL
;
516 const char *notify_key
;
522 new_generation
= state
->generation_count
;
524 SC_log(LOG_DEBUG
, "Network information updated: %llu",
527 bytes
= (const UInt8
*)state
;
528 len
= nwi_state_size(state
);
530 new_nwi_info
= CFDataCreate(NULL
, bytes
, len
);
533 dispatch_sync(_nwi_state_server_queue(), ^{
534 _libSC_info_server_set_data(&S_nwi_info
, new_nwi_info
, new_generation
);
537 if (new_nwi_info
!= NULL
) {
538 CFRelease(new_nwi_info
);
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
);
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
) {
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
571 main(int argc
, char **argv
)
573 static Boolean verbose
= (argc
> 1) ? TRUE
: FALSE
;
575 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
578 load_NetworkInformation(CFBundleGetMainBundle(), // bundle
579 ^(Boolean inSync
) { // sync handler
582 inSync
? "Yes" : "No")