2 * Copyright (c) 2012-2014 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 <CommonCrypto/CommonDigest.h>
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <SystemConfiguration/SCPrivate.h>
38 #include "libSystemConfiguration_client.h"
39 #include "libSystemConfiguration_server.h"
41 #include <network_information.h>
42 #include "network_information_priv.h"
43 #include "network_information_server.h"
53 * Note: all accesses should be made while running on the _nwi_server_queue()
55 static libSC_info_server_t S_nwi_info
;
60 * A boolean that enables additional logging.
62 static Boolean
*S_debug
= NULL
;
63 static SCLoggerRef S_logger
= NULL
;
68 * ACK (in-sync or not-in-sync) updates should be posted using
71 * Note: all accesses should be made while running on the _nwi_server_queue()
73 static _nwi_sync_handler_t S_sync_handler
= NULL
;
77 #pragma mark Support functions
82 log_xpc_object(const char *msg
, xpc_object_t obj
)
86 desc
= xpc_copy_description(obj
);
88 SCLoggerLog(S_logger
, LOG_DEBUG
, "%s = %s", msg
, desc
);
96 #pragma mark Network information server "main"
99 static dispatch_queue_t
100 _nwi_state_server_queue()
102 static dispatch_once_t once
;
103 static dispatch_queue_t q
;
105 dispatch_once(&once
, ^{
106 q
= dispatch_queue_create(NWI_SERVICE_NAME
".server", NULL
);
116 * Called when a client wants a copy of the current
117 * Network information
119 * - caller must be running on the _nwi_server_queue()
122 _nwi_state_copy(xpc_connection_t connection
, xpc_object_t request
)
126 xpc_connection_t remote
;
129 remote
= xpc_dictionary_get_remote_connection(request
);
130 reply
= xpc_dictionary_create_reply(request
);
132 SCLoggerLog(S_logger
, LOG_ERR
,
133 CFSTR("<%p> _nwi_state_copy: xpc_dictionary_create_reply: failed"),
138 // extract data and generation #
139 data
= _libSC_info_server_get_data(&S_nwi_info
, connection
, &generation
);
142 const char *proc_name
;
144 // extract process name
145 proc_name
= xpc_dictionary_get_string(request
, NWI_PROC_NAME
);
146 if (proc_name
== NULL
) {
150 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("<%p:%s[%d]> Network information copy: %llu"),
153 xpc_connection_get_pid(connection
),
157 // return the Network information (if available)
159 xpc_dictionary_set_data(reply
,
161 CFDataGetBytePtr(data
),
162 CFDataGetLength(data
));
166 xpc_connection_send_message(remote
, reply
);
174 * _nwi_state_acknowledge
176 * Called when a client wants to acknowledge processing
177 * of the Network information
179 * - caller must be running on the _nwi_server_queue()
182 _nwi_state_acknowledge(xpc_connection_t connection
, xpc_object_t request
)
187 generation
= xpc_dictionary_get_uint64(request
, NWI_GENERATION
);
190 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("<%p:%d> Network information ack: %llu"),
192 xpc_connection_get_pid(connection
),
196 _libSC_info_server_acknowledged(&S_nwi_info
, connection
, generation
);
197 changed
= _libSC_info_server_acknowledged(&S_nwi_info
, connection
, generation
);
202 inSync
= _libSC_info_server_in_sync(&S_nwi_info
);
203 S_sync_handler(inSync
);
211 process_request(xpc_connection_t connection
, xpc_object_t request
)
215 op
= xpc_dictionary_get_int64(request
, NWI_REQUEST
);
217 case NWI_REQUEST_COPY
:
219 * Return the Network information
221 _nwi_state_copy(connection
, request
);
224 case NWI_REQUEST_ACKNOWLEDGE
:
226 * Acknowlege a [processed] Network information
228 _nwi_state_acknowledge(connection
, request
);
232 SCLoggerLog(S_logger
, LOG_ERR
,
233 CFSTR("<%p> unknown request : %lld"),
245 process_new_connection(xpc_connection_t c
)
248 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("<%p:%d> Network information session: open"),
250 xpc_connection_get_pid(c
));
253 _libSC_info_server_open(&S_nwi_info
, c
);
255 xpc_connection_set_target_queue(c
, _nwi_state_server_queue());
257 xpc_connection_set_event_handler(c
, ^(xpc_object_t xobj
) {
260 type
= xpc_get_type(xobj
);
261 if (type
== XPC_TYPE_DICTIONARY
) {
262 // process the request
263 process_request(c
, xobj
);
265 } else if (type
== XPC_TYPE_ERROR
) {
268 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
269 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
273 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("<%p:%d> Network information session: close"),
275 xpc_connection_get_pid(c
));
278 changed
= _libSC_info_server_close(&S_nwi_info
, c
);
283 inSync
= _libSC_info_server_in_sync(&S_nwi_info
);
284 S_sync_handler(inSync
);
287 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
288 SCLoggerLog(S_logger
, LOG_ERR
,
291 xpc_connection_get_pid(c
),
295 SCLoggerLog(S_logger
, LOG_ERR
,
296 CFSTR("<%p:%d> Connection error: %p : %s"),
298 xpc_connection_get_pid(c
),
304 SCLoggerLog(S_logger
, LOG_ERR
,
305 CFSTR("<%p:%d> unknown event type : %p"),
307 xpc_connection_get_pid(c
),
312 xpc_connection_resume(c
);
319 #pragma mark Network Information server SPIs
324 load_NetworkInformation(CFBundleRef bundle
,
326 Boolean
*bundleVerbose
,
327 _nwi_sync_handler_t syncHandler
)
332 S_debug
= bundleVerbose
;
336 * keep track of Network information acknowledgements
338 _libSC_info_server_init(&S_nwi_info
);
341 * save the in-sync/not-in-sync handler
343 S_sync_handler
= Block_copy(syncHandler
);
345 // create XPC listener
346 name
= getenv(NWI_SERVICE_NAME
);
348 name
= NWI_SERVICE_NAME
;
351 c
= xpc_connection_create_mach_service(name
,
352 _nwi_state_server_queue(),
353 XPC_CONNECTION_MACH_SERVICE_LISTENER
);
355 xpc_connection_set_event_handler(c
, ^(xpc_object_t event
) {
358 type
= xpc_get_type(event
);
359 if (type
== XPC_TYPE_CONNECTION
) {
360 process_new_connection(event
);
362 } else if (type
== XPC_TYPE_ERROR
) {
365 desc
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
366 if (event
== XPC_ERROR_CONNECTION_INVALID
) {
367 SCLoggerLog(S_logger
, LOG_ERR
, CFSTR("Network information server: %s"), desc
);
369 } else if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
370 SCLoggerLog(S_logger
, LOG_ERR
, CFSTR("Network information server: %s"), desc
);
372 SCLoggerLog(S_logger
, LOG_ERR
,
373 CFSTR("Network information server: Connection error: %p : %s"),
379 SCLoggerLog(S_logger
, LOG_ERR
,
380 CFSTR("Network information server: unknown event type : %p"),
386 xpc_connection_resume(c
);
388 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("XPC server \"%s\" started"), name
);
396 _nwi_state_signature(nwi_state
*state
,
397 unsigned char *signature
,
398 size_t signature_len
)
400 bzero(signature
, signature_len
);
404 uint64_t generation_save
;
406 unsigned char sha1_buf
[CC_SHA1_DIGEST_LENGTH
];
408 generation_save
= state
->generation_count
;
409 state
->generation_count
= 0;
411 sha1
= (signature_len
>= CC_SHA1_DIGEST_LENGTH
) ? signature
: sha1_buf
;
416 CC_SHA1_Final(sha1
, &ctx
);
417 if (sha1
!= signature
) {
418 bcopy(sha1
, signature
, signature_len
);
421 state
->generation_count
= generation_save
;
430 _nwi_state_store(nwi_state
*state
)
433 uint64_t new_generation
= 0;
434 CFDataRef new_nwi_info
= NULL
;
435 const char *notify_key
;
441 new_generation
= state
->generation_count
;
444 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("Network information updated: %llu"),
448 bytes
= (const UInt8
*)state
;
451 new_nwi_info
= CFDataCreate(NULL
, bytes
, len
);
454 dispatch_sync(_nwi_state_server_queue(), ^{
455 _libSC_info_server_set_data(&S_nwi_info
, new_nwi_info
, new_generation
);
458 if (new_nwi_info
!= NULL
) {
459 CFRelease(new_nwi_info
);
462 // if anyone is keeping us in sync, they now need to catchup
463 in_sync
= _libSC_info_server_in_sync(&S_nwi_info
);
464 S_sync_handler(in_sync
);
466 // and let everyone else know that the configuration has been updated
467 notify_key
= nwi_state_get_notify_key();
468 if (notify_key
!= NULL
) {
471 _nwi_state_force_refresh();
472 status
= notify_post(notify_key
);
473 if (status
!= NOTIFY_STATUS_OK
) {
474 SCLoggerLog(S_logger
, LOG_ERR
, CFSTR("notify_post() failed: %d"), status
);
475 // notification posting failures are non-fatal
490 main(int argc
, char **argv
)
492 static Boolean verbose
= (argc
> 1) ? TRUE
: FALSE
;
494 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
497 load_NetworkInformation(CFBundleGetMainBundle(), // bundle
499 &verbose
, // bundleVerbose
500 ^(Boolean inSync
) { // sync handler
501 SCLoggerLog(NULL
, LOG_INFO
,
502 CFSTR("in sync: %s"),
503 inSync
? "Yes" : "No");