2 * Copyright (c) 2005-2007, 2009 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 * NetworkIdentification.c
26 * - maintains a history of networks that the system has connected to by
27 * watching the Network Services that post data to the SCDynamicStore
31 * Modification History
33 * November 9, 2006 Dieter Siegmund (dieter@apple.com)
38 #include <SystemConfiguration/SystemConfiguration.h>
39 #include <SystemConfiguration/SCValidation.h>
40 #include <SystemConfiguration/SCPrivate.h>
41 #include <CoreFoundation/CFDictionary.h>
42 #include <SystemConfiguration/SCNetworkSignature.h>
43 #include <SystemConfiguration/SCNetworkSignaturePrivate.h>
45 /* debug output on/off */
46 static Boolean S_NetworkIdentification_debug
;
48 /* should we bother keeping track of networks? */
49 static Boolean S_NetworkIdentification_disabled
;
51 typedef struct ServiceWatcher_s ServiceWatcher
, * ServiceWatcherRef
;
53 /* returns an array of currently available information */
55 ServiceWatcherCopyCurrent(ServiceWatcherRef watcher
);
57 static ServiceWatcherRef
58 ServiceWatcherCreate();
61 ServiceWatcherFree(ServiceWatcherRef
* watcher_p
);
63 /* XXX these should be made tunable */
64 #define SIGNATURE_HISTORY_MAX 150
65 #define SERVICE_HISTORY_MAX 5
67 /* don't re-write the prefs file unless this time interval has elapsed */
68 #define SIGNATURE_UPDATE_INTERVAL_SECS (24 * 3600) /* 24 hours */
70 struct ServiceWatcher_s
{
71 CFRunLoopSourceRef rls
;
72 SCDynamicStoreRef store
;
73 CFMutableArrayRef signatures
;
74 CFArrayRef active_signatures
;
75 CFStringRef primary_ipv4
;
76 CFStringRef setup_ipv4_key
;
77 CFStringRef state_ipv4_key
;
80 #define kIdentifier CFSTR("Identifier")
81 #define kService CFSTR("Service")
82 #define kServices CFSTR("Services")
83 #define kSignature CFSTR("Signature")
84 #define kSignatures CFSTR("Signatures")
85 #define kTimestamp CFSTR("Timestamp")
86 #define kServiceID CFSTR("ServiceID")
87 #define kNetworkSignature CFSTR("NetworkSignature")
88 #define kServiceIdentifiers kStoreKeyServiceIdentifiers
91 make_service_entity_pattern_array(CFStringRef
* keys
, int n_keys
)
96 for (i
= 0; i
< n_keys
; i
++) {
97 /* re-use the array that was passed in to get the pattern */
98 keys
[i
] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
99 kSCDynamicStoreDomainState
,
103 list
= CFArrayCreate(NULL
, (const void * *)keys
, n_keys
,
104 &kCFTypeArrayCallBacks
);
105 for (i
= 0; i
< n_keys
; i
++) {
106 /* then release the allocated patterns */
113 ServiceWatcherNotificationPatterns(void)
115 CFStringRef keys
[1] = { kSCEntNetIPv4
};
117 return (make_service_entity_pattern_array(keys
,
118 sizeof(keys
) / sizeof(keys
[0])));
122 ServiceWatcherPatterns(void)
124 CFStringRef keys
[2] = { kSCEntNetIPv4
, kSCEntNetDNS
};
126 return (make_service_entity_pattern_array(keys
,
127 sizeof(keys
) / sizeof(keys
[0])));
131 myCFDictionaryArrayGetValue(CFArrayRef array
, CFStringRef key
, CFTypeRef value
,
138 count
= CFArrayGetCount(array
);
143 for (i
= 0; i
< count
; i
++) {
144 CFDictionaryRef dict
;
147 dict
= CFArrayGetValueAtIndex(array
, i
);
148 if (isA_CFDictionary(dict
) == NULL
) {
151 this_val
= CFDictionaryGetValue(dict
, key
);
152 if (CFEqual(this_val
, value
)) {
153 if (ret_index
!= NULL
) {
160 if (ret_index
!= NULL
) {
166 static CFDictionaryRef
167 copy_airport_dict(SCDynamicStoreRef store
, CFStringRef if_name
)
169 CFDictionaryRef dict
;
172 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
173 kSCDynamicStoreDomainState
,
176 dict
= SCDynamicStoreCopyValue(store
, key
);
182 add_airport_info(SCDynamicStoreRef store
, CFMutableDictionaryRef dict
)
184 CFDictionaryRef airport_dict
= NULL
;
187 CFDictionaryRef simple_dict
;
190 if_name
= CFDictionaryGetValue(dict
, kSCPropInterfaceName
);
191 if (isA_CFString(if_name
) == NULL
) {
194 airport_dict
= copy_airport_dict(store
, if_name
);
195 if (airport_dict
== NULL
) {
199 value
= CFDictionaryGetValue(airport_dict
, key
);
204 CFDictionaryCreate(NULL
,
205 (const void * *)&key
, (const void * *)&value
, 1,
206 &kCFTypeDictionaryKeyCallBacks
,
207 &kCFTypeDictionaryValueCallBacks
);
208 CFDictionarySetValue(dict
, kSCEntNetAirPort
, simple_dict
);
209 CFRelease(simple_dict
);
212 if (airport_dict
!= NULL
) {
213 CFRelease(airport_dict
);
218 static CFDictionaryRef
219 get_current_dict(CFDictionaryRef current
, CFStringRef entity
,
220 CFArrayRef components
)
222 CFDictionaryRef dict
;
225 if (CFArrayGetCount(components
) < 5) {
226 /* this can't happen, we already checked */
229 key
= CFStringCreateWithFormat(NULL
, NULL
,
230 CFSTR("%@/%@/%@/%@/%@"),
231 CFArrayGetValueAtIndex(components
, 0),
232 CFArrayGetValueAtIndex(components
, 1),
233 CFArrayGetValueAtIndex(components
, 2),
234 CFArrayGetValueAtIndex(components
, 3),
236 dict
= CFDictionaryGetValue(current
, key
);
238 return (isA_CFDictionary(dict
));
242 process_dict(SCDynamicStoreRef store
, CFDictionaryRef current
)
244 CFMutableArrayRef array
= NULL
;
247 const void * * keys
= NULL
;
248 const void * * values
= NULL
;
250 count
= CFDictionaryGetCount(current
);
254 array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
255 keys
= (const void * *)malloc(sizeof(keys
) * count
);
256 values
= (const void * *)malloc(sizeof(values
) * count
);
257 CFDictionaryGetKeysAndValues(current
, keys
, values
);
258 for (i
= 0; i
< count
; i
++) {
259 CFArrayRef components
= NULL
;
260 CFDictionaryRef dns_dict
;
262 CFMutableDictionaryRef entity_dict
= NULL
;
263 CFMutableDictionaryRef new_dict
= NULL
;
264 CFStringRef sig_str
= NULL
;
265 CFMutableDictionaryRef service_dict
= NULL
;
266 CFStringRef serviceID
;
268 if (isA_CFDictionary(values
[i
]) == NULL
) {
271 components
= CFStringCreateArrayBySeparatingStrings(NULL
, keys
[i
],
273 if (components
== NULL
) {
276 if (CFArrayGetCount(components
) < 5) {
277 /* too few components */
280 entity
= CFArrayGetValueAtIndex(components
, 4);
281 if (!CFEqual(entity
, kSCEntNetIPv4
)) {
284 serviceID
= CFArrayGetValueAtIndex(components
, 3);
285 sig_str
= CFDictionaryGetValue(values
[i
], kNetworkSignature
);
286 if (isA_CFString(sig_str
) == NULL
287 || CFStringGetLength(sig_str
) == 0) {
290 /* create a new entry */
291 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
292 &kCFTypeDictionaryKeyCallBacks
,
293 &kCFTypeDictionaryValueCallBacks
);
294 CFDictionarySetValue(new_dict
, kSignature
, sig_str
);
295 service_dict
= CFDictionaryCreateMutable(NULL
, 0,
296 &kCFTypeDictionaryKeyCallBacks
,
297 &kCFTypeDictionaryValueCallBacks
);
298 CFDictionarySetValue(service_dict
, kServiceID
, serviceID
);
299 add_airport_info(store
, service_dict
);
300 entity_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, values
[i
]);
301 CFDictionaryRemoveValue(entity_dict
, kNetworkSignature
);
302 CFDictionarySetValue(service_dict
, kSCEntNetIPv4
, entity_dict
);
303 dns_dict
= get_current_dict(current
, kSCEntNetDNS
, components
);
304 if (dns_dict
!= NULL
) {
305 CFDictionarySetValue(service_dict
, kSCEntNetDNS
, dns_dict
);
307 CFDictionarySetValue(new_dict
, kService
, service_dict
);
308 CFArrayAppendValue(array
, new_dict
);
311 if (entity_dict
!= NULL
) {
312 CFRelease(entity_dict
);
314 if (service_dict
!= NULL
) {
315 CFRelease(service_dict
);
317 if (components
!= NULL
) {
318 CFRelease(components
);
320 if (new_dict
!= NULL
) {
324 count
= CFArrayGetCount(array
);
335 if (values
!= NULL
) {
343 ServiceWatcherCopyCurrent(ServiceWatcherRef watcher
)
345 CFDictionaryRef current
;
347 CFArrayRef ret
= NULL
;
349 list
= ServiceWatcherPatterns();
350 current
= SCDynamicStoreCopyMultiple(watcher
->store
, NULL
, list
);
352 if (current
== NULL
) {
355 ret
= process_dict(watcher
->store
, current
);
357 if (current
!= NULL
) {
364 ServiceWatcherSetActiveSignatures(ServiceWatcherRef watcher
, CFArrayRef active
)
366 Boolean changed
= FALSE
;
367 CFArrayRef prev_active
;
369 prev_active
= watcher
->active_signatures
;
370 if (prev_active
== NULL
&& active
== NULL
) {
374 if (prev_active
!= NULL
&& active
!= NULL
) {
375 changed
= !CFEqual(prev_active
, active
);
380 if (active
!= NULL
) {
383 if (prev_active
!= NULL
) {
384 CFRelease(prev_active
);
386 watcher
->active_signatures
= active
;
388 if (active
!= NULL
) {
389 SCLog(S_NetworkIdentification_debug
,
390 LOG_NOTICE
, CFSTR("Active Signatures %@"), active
);
393 SCLog(S_NetworkIdentification_debug
,
394 LOG_NOTICE
, CFSTR("No Active Signatures"));
402 ServiceWatcherSetPrimaryIPv4(ServiceWatcherRef watcher
,
403 CFStringRef primary_ipv4
)
405 Boolean changed
= FALSE
;
406 CFStringRef prev_ipv4_primary
;
408 prev_ipv4_primary
= watcher
->primary_ipv4
;
409 if (prev_ipv4_primary
== NULL
&& primary_ipv4
== NULL
) {
413 if (prev_ipv4_primary
!= NULL
&& primary_ipv4
!= NULL
) {
414 changed
= !CFEqual(prev_ipv4_primary
, primary_ipv4
);
419 if (primary_ipv4
!= NULL
) {
420 CFRetain(primary_ipv4
);
422 if (prev_ipv4_primary
!= NULL
) {
423 CFRelease(prev_ipv4_primary
);
425 watcher
->primary_ipv4
= primary_ipv4
;
427 if (primary_ipv4
!= NULL
) {
428 SCLog(S_NetworkIdentification_debug
,
429 LOG_NOTICE
, CFSTR("Primary IPv4 %@"), primary_ipv4
);
432 SCLog(S_NetworkIdentification_debug
, LOG_NOTICE
,
433 CFSTR("No Primary IPv4"));
441 static CFDictionaryRef
442 signature_add_service(CFDictionaryRef sig_dict
, CFDictionaryRef service
,
443 CFArrayRef active_services
)
446 CFMutableDictionaryRef new_dict
= NULL
;
449 list
= CFDictionaryGetValue(sig_dict
, kServices
);
450 now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
452 list
= CFArrayCreate(NULL
, (const void * *)&service
, 1,
453 &kCFTypeArrayCallBacks
);
456 int list_count
= CFArrayGetCount(list
);
457 CFMutableArrayRef new_list
= NULL
;
458 CFRange range
= CFRangeMake(0, list_count
);
461 where
= CFArrayGetFirstIndexOfValue(list
, range
, service
);
462 if (where
!= kCFNotFound
) {
465 date
= CFDictionaryGetValue(sig_dict
, kTimestamp
);
467 CFTimeInterval time_interval
;
469 time_interval
= CFDateGetTimeIntervalSinceDate(now
, date
);
470 /* don't bother updating timestamp until interval has passed */
471 if (time_interval
< (SIGNATURE_UPDATE_INTERVAL_SECS
)) {
476 /* it's already in the right place */
482 new_list
= CFArrayCreateMutableCopy(NULL
, 0, list
);
483 if (where
!= kCFNotFound
) {
484 CFArrayRemoveValueAtIndex(new_list
, where
);
489 CFArrayInsertValueAtIndex(new_list
, 0, service
);
490 /* try to remove stale entries */
491 if (list_count
> SERVICE_HISTORY_MAX
) {
493 int remove_count
= list_count
- SERVICE_HISTORY_MAX
;
495 SCLog(S_NetworkIdentification_debug
,
496 LOG_NOTICE
, CFSTR("Attempting to remove %d services"),
498 for (i
= list_count
- 1; i
>= 0 && remove_count
> 0; i
--) {
499 CFDictionaryRef dict
;
501 dict
= CFArrayGetValueAtIndex(new_list
, i
);
502 if (myCFDictionaryArrayGetValue(active_services
,
503 kService
, dict
, NULL
)
505 /* skip anything that's currently active */
506 SCLog(S_NetworkIdentification_debug
,
507 LOG_NOTICE
, CFSTR("Skipping Service %@"),
511 SCLog(S_NetworkIdentification_debug
, LOG_NOTICE
,
512 CFSTR("Removing Service %@"), dict
);
513 CFArrayRemoveValueAtIndex(new_list
, i
);
518 list
= (CFArrayRef
)new_list
;
523 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, sig_dict
);
525 CFDictionarySetValue(new_dict
, kServices
, list
);
528 CFDictionarySetValue(new_dict
, kTimestamp
, now
);
535 #define ARBITRARILY_LARGE_NUMBER (1024 * 1024)
537 get_best_serviceID(CFArrayRef serviceID_list
, CFArrayRef order
)
540 CFStringRef best_serviceID
;
545 count
= CFArrayGetCount(serviceID_list
);
546 if (count
== 1 || order
== NULL
) {
547 return (CFArrayGetValueAtIndex(serviceID_list
, 0));
549 best_serviceID
= NULL
;
550 best_rank
= ARBITRARILY_LARGE_NUMBER
;
551 range
= CFRangeMake(0, CFArrayGetCount(order
));
552 for (i
= 0; i
< count
; i
++) {
553 CFStringRef serviceID
= CFArrayGetValueAtIndex(serviceID_list
, i
);
556 this_rank
= CFArrayGetFirstIndexOfValue(order
, range
, serviceID
);
557 if (this_rank
== kCFNotFound
) {
558 this_rank
= ARBITRARILY_LARGE_NUMBER
;
560 if (best_serviceID
== NULL
|| this_rank
< best_rank
) {
561 best_serviceID
= serviceID
;
562 best_rank
= this_rank
;
565 return (best_serviceID
);
569 copy_service_order(SCDynamicStoreRef session
, CFStringRef ipv4_key
)
571 CFArrayRef order
= NULL
;
572 CFDictionaryRef ipv4_dict
= NULL
;
574 if (session
== NULL
) {
577 ipv4_dict
= SCDynamicStoreCopyValue(session
, ipv4_key
);
578 if (isA_CFDictionary(ipv4_dict
) != NULL
) {
579 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
580 order
= isA_CFArray(order
);
585 if (ipv4_dict
!= NULL
) {
586 CFRelease(ipv4_dict
);
591 typedef struct service_order_with_range
{
592 CFArrayRef service_order
;
594 } service_order_with_range_t
;
597 add_netID_and_serviceID(service_order_with_range_t
* order
, int count
,
598 CFMutableArrayRef netID_list
, CFStringRef netID
,
599 CFMutableArrayRef serviceID_list
, CFStringRef serviceID
)
605 if (count
== 0 || order
->service_order
== NULL
) {
608 serviceID_index
= CFArrayGetFirstIndexOfValue(order
->service_order
,
611 if (serviceID_index
== kCFNotFound
) {
614 for (i
= 0; i
< count
; i
++) {
615 CFStringRef scan
= CFArrayGetValueAtIndex(serviceID_list
, i
);
618 scan_index
= CFArrayGetFirstIndexOfValue(order
->service_order
,
621 if (scan_index
== kCFNotFound
622 || serviceID_index
< scan_index
) {
623 /* found our insertion point */
624 CFArrayInsertValueAtIndex(netID_list
, i
, netID
);
625 CFArrayInsertValueAtIndex(serviceID_list
, i
, serviceID
);
631 CFArrayAppendValue(netID_list
, netID
);
632 CFArrayAppendValue(serviceID_list
, serviceID
);
637 ServiceWatcherPublishActiveIdentifiers(ServiceWatcherRef watcher
)
639 Boolean updated
= FALSE
;
641 if (watcher
->active_signatures
== NULL
) {
642 CFDictionaryRef dict
;
644 dict
= SCDynamicStoreCopyValue(watcher
->store
,
645 kSCNetworkIdentificationStoreKey
);
648 SCLog(S_NetworkIdentification_debug
,
649 LOG_NOTICE
, CFSTR("Removing %@"),
650 kSCNetworkIdentificationStoreKey
);
651 SCDynamicStoreRemoveValue(watcher
->store
,
652 kSCNetworkIdentificationStoreKey
);
658 CFDictionaryRef dict
;
660 CFMutableArrayRef id_list
;
663 service_order_with_range_t order
;
664 CFStringRef primary_ipv4_id
= NULL
;
665 CFMutableArrayRef serviceID_list
;
666 CFDictionaryRef store_dict
;
669 order
.service_order
= copy_service_order(watcher
->store
,
670 watcher
->setup_ipv4_key
);
671 if (order
.service_order
!= NULL
) {
672 order
.range
= CFRangeMake(0, CFArrayGetCount(order
.service_order
));
674 id_list
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
675 serviceID_list
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
676 count
= CFArrayGetCount(watcher
->active_signatures
);
677 for (i
= 0; i
< count
; i
++) {
679 CFStringRef serviceID
;
680 CFArrayRef this_list
;
682 dict
= CFArrayGetValueAtIndex(watcher
->active_signatures
, i
);
683 this_id
= CFDictionaryGetValue(dict
, kIdentifier
);
684 this_list
= CFDictionaryGetValue(dict
, kServiceIdentifiers
);
685 if (primary_ipv4_id
== NULL
&& watcher
->primary_ipv4
!= NULL
) {
688 range
= CFRangeMake(0, CFArrayGetCount(this_list
));
689 if (CFArrayContainsValue(this_list
, range
,
690 watcher
->primary_ipv4
)) {
691 primary_ipv4_id
= this_id
;
694 serviceID
= get_best_serviceID(this_list
, order
.service_order
);
695 add_netID_and_serviceID(&order
, i
, id_list
, this_id
,
696 serviceID_list
, serviceID
);
698 keys
[0] = kStoreKeyActiveIdentifiers
;
700 keys
[1] = kStoreKeyServiceIdentifiers
;
701 values
[1] = serviceID_list
;
702 if (primary_ipv4_id
!= NULL
) {
704 keys
[2] = kStoreKeyPrimaryIPv4Identifier
;
705 values
[2] = primary_ipv4_id
;
710 dict
= CFDictionaryCreate(NULL
, (const void * *)keys
,
711 (const void * *)values
, keys_count
,
712 &kCFTypeDictionaryKeyCallBacks
,
713 &kCFTypeDictionaryValueCallBacks
);
715 = SCDynamicStoreCopyValue(watcher
->store
,
716 kSCNetworkIdentificationStoreKey
);
717 if (isA_CFDictionary(store_dict
) == NULL
718 || CFEqual(store_dict
, dict
) == FALSE
) {
720 SCDynamicStoreSetValue(watcher
->store
,
721 kSCNetworkIdentificationStoreKey
, dict
);
722 SCLog(S_NetworkIdentification_debug
,
723 LOG_NOTICE
, CFSTR("Setting %@ = %@"),
724 kSCNetworkIdentificationStoreKey
,
728 SCLog(S_NetworkIdentification_debug
,
729 LOG_NOTICE
, CFSTR("Not setting %@"),
730 kSCNetworkIdentificationStoreKey
);
734 CFRelease(serviceID_list
);
735 if (order
.service_order
!= NULL
) {
736 CFRelease(order
.service_order
);
738 if (store_dict
!= NULL
) {
739 CFRelease(store_dict
);
745 static CFDictionaryRef
746 signature_dict_create(CFStringRef this_sig
, CFDictionaryRef service
)
748 CFDictionaryRef dict
;
749 const void * keys
[4];
750 const void * values
[4];
752 keys
[0] = kSignature
;
753 values
[0] = this_sig
;
756 values
[1] = CFArrayCreate(NULL
, (const void * *)&service
, 1,
757 &kCFTypeArrayCallBacks
);
758 keys
[2] = kIdentifier
;
759 values
[2] = this_sig
;
761 keys
[3] = kTimestamp
;
762 values
[3] = CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
764 dict
= CFDictionaryCreate(NULL
, keys
, values
,
765 sizeof(keys
) / sizeof(keys
[0]),
766 &kCFTypeDictionaryKeyCallBacks
,
767 &kCFTypeDictionaryValueCallBacks
);
768 CFRelease(values
[1]);
769 CFRelease(values
[3]);
774 ServiceWatcherRemoveStaleSignatures(ServiceWatcherRef watcher
)
776 int active_count
= 0;
781 count
= CFArrayGetCount(watcher
->signatures
);
782 if (watcher
->active_signatures
!= NULL
) {
783 active_count
= CFArrayGetCount(watcher
->active_signatures
);
785 if ((count
- active_count
) <= SIGNATURE_HISTORY_MAX
) {
788 remove_count
= count
- active_count
- SIGNATURE_HISTORY_MAX
;
789 for (i
= count
- 1; i
>= 0 && remove_count
> 0; i
--) {
790 CFDictionaryRef sig_dict
;
793 sig_dict
= CFArrayGetValueAtIndex(watcher
->signatures
, i
);
794 sig_str
= CFDictionaryGetValue(sig_dict
, kSignature
);
796 if (myCFDictionaryArrayGetValue(watcher
->active_signatures
,
797 kSignature
, sig_str
, NULL
)
799 /* skip anything that's currently active */
800 SCLog(S_NetworkIdentification_debug
,
801 LOG_NOTICE
, CFSTR("Skipping %@"), sig_dict
);
804 SCLog(S_NetworkIdentification_debug
,
805 LOG_NOTICE
, CFSTR("ServiceWatcher: Removing %@"),
807 CFArrayRemoveValueAtIndex(watcher
->signatures
, i
);
815 ServiceWatcherSaveSignatures(ServiceWatcherRef watcher
)
817 SCPreferencesRef prefs
;
819 prefs
= SCPreferencesCreate(NULL
, CFSTR("ServiceWatcher"),
820 kSCNetworkIdentificationPrefsKey
);
822 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ServiceWatcherSaveSignatures: Create failed %s"),
823 SCErrorString(SCError()));
826 ServiceWatcherRemoveStaleSignatures(watcher
);
827 if (SCPreferencesSetValue(prefs
, kSignatures
, watcher
->signatures
)
829 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ServiceWatcherSaveSignatures: Set failed %s"),
830 SCErrorString(SCError()));
832 else if (SCPreferencesCommitChanges(prefs
) == FALSE
) {
833 // An EROFS error is expected during installation. All other
834 // errors should be reported.
835 if (SCError() != EROFS
) {
836 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ServiceWatcherSaveSignatures: Commit failed %s"),
837 SCErrorString(SCError()));
846 ServiceWatcherLoadSignatures(ServiceWatcherRef watcher
)
850 SCPreferencesRef prefs
;
851 CFArrayRef signatures
;
854 = CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
855 prefs
= SCPreferencesCreate(NULL
, CFSTR("ServiceWatcher"),
856 kSCNetworkIdentificationPrefsKey
);
858 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ServiceWatcherLoadSignatures: Create failed %s"),
859 SCErrorString(SCError()));
862 signatures
= SCPreferencesGetValue(prefs
, kSignatures
);
863 if (signatures
== NULL
) {
866 if (isA_CFArray(signatures
) == NULL
) {
867 SCLog(TRUE
, LOG_NOTICE
,
868 CFSTR("ServiceWatcherLoadSignatures: Signatures is not an array"));
871 count
= CFArrayGetCount(signatures
);
872 for (i
= 0; i
< count
; i
++) {
873 CFDictionaryRef dict
;
879 dict
= CFArrayGetValueAtIndex(signatures
, i
);
880 if (isA_CFDictionary(dict
) == NULL
) {
883 sig_id
= CFDictionaryGetValue(dict
, kIdentifier
);
884 if (isA_CFString(sig_id
) == NULL
) {
887 sig_str
= CFDictionaryGetValue(dict
, kSignature
);
888 if (isA_CFString(sig_str
) == NULL
) {
891 timestamp
= CFDictionaryGetValue(dict
, kTimestamp
);
892 if (isA_CFDate(timestamp
) == NULL
) {
895 services
= CFDictionaryGetValue(dict
, kServices
);
896 if (isA_CFArray(services
) == NULL
) {
899 CFArrayAppendValue(watcher
->signatures
, dict
);
909 ServiceWatcherUpdate(ServiceWatcherRef watcher
, Boolean update_signatures
)
911 CFMutableArrayRef active_signatures
= NULL
;
914 Boolean save_signatures
= FALSE
;
915 CFArrayRef service_list
;
916 Boolean update_store
= FALSE
;
918 service_list
= ServiceWatcherCopyCurrent(watcher
);
919 SCLog(S_NetworkIdentification_debug
,
920 LOG_NOTICE
, CFSTR("service_list = %@"), service_list
);
921 if (service_list
== NULL
) {
924 active_signatures
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
925 count
= CFArrayGetCount(service_list
);
926 for (i
= 0; i
< count
; i
++) {
927 CFDictionaryRef dict
;
928 CFDictionaryRef active_dict
;
930 CFMutableDictionaryRef new_active_dict
;
931 CFDictionaryRef new_sig_dict
;
932 CFStringRef serviceID
;
934 CFDictionaryRef service
;
935 CFDictionaryRef sig_dict
;
936 CFStringRef this_sig
;
939 dict
= CFArrayGetValueAtIndex(service_list
, i
);
940 service
= CFDictionaryGetValue(dict
, kService
);
941 this_sig
= CFDictionaryGetValue(dict
, kSignature
);
942 if (this_sig
== NULL
) {
943 /* service has no signature */
946 sig_dict
= myCFDictionaryArrayGetValue(watcher
->signatures
, kSignature
,
948 if (sig_dict
== NULL
) {
949 /* add a new signature entry */
950 sig_dict
= signature_dict_create(this_sig
, service
);
951 CFArrayInsertValueAtIndex(watcher
->signatures
, 0, sig_dict
);
953 save_signatures
= TRUE
;
954 sig_id
= CFDictionaryGetValue(sig_dict
, kIdentifier
);
958 /* update an existing signature entry */
960 sig_id
= CFDictionaryGetValue(sig_dict
, kIdentifier
);
961 new_sig_dict
= signature_add_service(sig_dict
, service
,
963 if (new_sig_dict
!= NULL
) {
964 CFArrayRemoveValueAtIndex(watcher
->signatures
, where
);
965 CFArrayInsertValueAtIndex(watcher
->signatures
, 0,
967 CFRelease(new_sig_dict
);
968 save_signatures
= TRUE
;
971 = myCFDictionaryArrayGetValue(active_signatures
,
972 kSignature
, this_sig
,
975 if (active_dict
== NULL
) {
976 /* signature now active, this is the first/only service */
978 = CFDictionaryCreateMutable(NULL
, 0,
979 &kCFTypeDictionaryKeyCallBacks
,
980 &kCFTypeDictionaryValueCallBacks
);
981 CFDictionarySetValue(new_active_dict
, kSignature
, this_sig
);
982 CFDictionarySetValue(new_active_dict
, kIdentifier
, sig_id
);
983 serviceID
= CFDictionaryGetValue(service
, kServiceID
);
984 id_list
= CFArrayCreate(NULL
, (const void * *)&serviceID
, 1,
985 &kCFTypeArrayCallBacks
);
986 CFDictionarySetValue(new_active_dict
, kServiceIdentifiers
,
988 CFArrayAppendValue(active_signatures
, new_active_dict
);
989 CFRelease(new_active_dict
);
993 /* signature already active, add this serviceID */
996 id_list
= CFDictionaryGetValue(active_dict
,
997 kServiceIdentifiers
);
998 range
= CFRangeMake(0, CFArrayGetCount(id_list
));
999 serviceID
= CFDictionaryGetValue(service
, kServiceID
);
1000 if (CFArrayContainsValue(id_list
, range
, serviceID
) == FALSE
) {
1001 CFMutableDictionaryRef new_active_dict
;
1002 CFMutableArrayRef new_id_list
;
1004 new_id_list
= CFArrayCreateMutableCopy(NULL
, 0, id_list
);
1005 CFArrayAppendValue(new_id_list
, serviceID
);
1007 = CFDictionaryCreateMutableCopy(NULL
, 0, active_dict
);
1008 CFDictionarySetValue(new_active_dict
, kServiceIdentifiers
,
1010 CFArraySetValueAtIndex(active_signatures
, where
,
1012 CFRelease(new_active_dict
);
1013 CFRelease(new_id_list
);
1018 if (active_signatures
== NULL
1019 || CFArrayGetCount(active_signatures
) == 0) {
1021 = ServiceWatcherSetActiveSignatures(watcher
, NULL
);
1025 = ServiceWatcherSetActiveSignatures(watcher
, active_signatures
);
1027 if (save_signatures
) {
1028 /* write out the file */
1029 ServiceWatcherSaveSignatures(watcher
);
1032 if (service_list
!= NULL
) {
1033 CFRelease(service_list
);
1035 if (active_signatures
!= NULL
) {
1036 CFRelease(active_signatures
);
1038 if (update_signatures
|| update_store
) {
1039 if (ServiceWatcherPublishActiveIdentifiers(watcher
)) {
1040 notify_post(kSCNetworkSignatureActiveChangedNotifyName
);
1047 update_primary_ipv4(ServiceWatcherRef watcher
)
1049 Boolean changed
= FALSE
;
1050 CFDictionaryRef global_ipv4
;
1052 global_ipv4
= SCDynamicStoreCopyValue(watcher
->store
,
1053 watcher
->state_ipv4_key
);
1054 if (isA_CFDictionary(global_ipv4
) != NULL
) {
1055 CFStringRef primary_ipv4
;
1058 = CFDictionaryGetValue(global_ipv4
,
1059 kSCDynamicStorePropNetPrimaryService
);
1060 changed
= ServiceWatcherSetPrimaryIPv4(watcher
,
1061 isA_CFString(primary_ipv4
));
1063 if (global_ipv4
!= NULL
) {
1064 CFRelease(global_ipv4
);
1070 ServiceWatcherNotifier(SCDynamicStoreRef not_used
, CFArrayRef changes
,
1075 Boolean order_changed
= FALSE
;
1076 Boolean global_ipv4_changed
= FALSE
;
1077 Boolean primary_ipv4_changed
= FALSE
;
1078 ServiceWatcherRef watcher
= (ServiceWatcherRef
)info
;
1080 count
= CFArrayGetCount(changes
);
1084 for (i
= 0; i
< count
; i
++) {
1085 CFStringRef key
= CFArrayGetValueAtIndex(changes
, i
);
1087 if (CFStringHasPrefix(key
, kSCDynamicStoreDomainSetup
)) {
1088 order_changed
= TRUE
;
1090 else if (CFEqual(key
, watcher
->state_ipv4_key
)) {
1091 global_ipv4_changed
= TRUE
;
1094 if (global_ipv4_changed
) {
1095 primary_ipv4_changed
= update_primary_ipv4(watcher
);
1098 && (order_changed
|| primary_ipv4_changed
)) {
1099 /* just the service order or the primary service changed */
1100 if (ServiceWatcherPublishActiveIdentifiers(watcher
)) {
1101 notify_post(kSCNetworkSignatureActiveChangedNotifyName
);
1105 ServiceWatcherUpdate(watcher
, order_changed
|| primary_ipv4_changed
);
1110 static ServiceWatcherRef
1111 ServiceWatcherCreate()
1113 SCDynamicStoreContext context
= { 0, 0, 0, 0, 0};
1114 CFArrayRef patterns
;
1115 CFStringRef keys
[2];
1116 CFArrayRef key_list
;
1117 ServiceWatcherRef watcher
;
1119 watcher
= malloc(sizeof(*watcher
));
1120 bzero(watcher
, sizeof(*watcher
));
1121 context
.info
= watcher
;
1122 watcher
->store
= SCDynamicStoreCreate(NULL
, CFSTR("Service Watcher"),
1123 ServiceWatcherNotifier
, &context
);
1124 if (watcher
->store
== NULL
) {
1125 SCLog(TRUE
, LOG_NOTICE
, CFSTR("SCDynamicStoreCreate failed: %s"),
1126 SCErrorString(SCError()));
1129 watcher
->setup_ipv4_key
1130 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
1131 kSCDynamicStoreDomainSetup
,
1133 watcher
->state_ipv4_key
1134 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
1135 kSCDynamicStoreDomainState
,
1137 keys
[0] = watcher
->setup_ipv4_key
;
1138 keys
[1] = watcher
->state_ipv4_key
;
1139 key_list
= CFArrayCreate(NULL
, (const void * *)keys
, sizeof(keys
) / sizeof(keys
[0]),
1140 &kCFTypeArrayCallBacks
);
1141 patterns
= ServiceWatcherNotificationPatterns();
1142 (void)SCDynamicStoreSetNotificationKeys(watcher
->store
, key_list
, patterns
);
1143 CFRelease(patterns
);
1144 CFRelease(key_list
);
1145 watcher
->rls
= SCDynamicStoreCreateRunLoopSource(NULL
, watcher
->store
, 0);
1146 CFRunLoopAddSource(CFRunLoopGetCurrent(), watcher
->rls
,
1147 kCFRunLoopDefaultMode
);
1148 ServiceWatcherLoadSignatures(watcher
);
1149 update_primary_ipv4(watcher
);
1152 ServiceWatcherFree(&watcher
);
1157 ServiceWatcherFree(ServiceWatcherRef
* watcher_p
)
1159 ServiceWatcherRef watcher
;
1161 if (watcher_p
== NULL
) {
1164 watcher
= *watcher_p
;
1165 if (watcher
== NULL
) {
1169 if (watcher
->store
!= NULL
) {
1170 CFRelease(watcher
->store
);
1171 watcher
->store
= NULL
;
1173 if (watcher
->rls
!= NULL
) {
1174 CFRunLoopSourceInvalidate(watcher
->rls
);
1175 CFRelease(watcher
->rls
);
1176 watcher
->rls
= NULL
;
1178 if (watcher
->signatures
!= NULL
) {
1179 CFRelease(watcher
->signatures
);
1180 watcher
->signatures
= NULL
;
1182 if (watcher
->state_ipv4_key
!= NULL
) {
1183 CFRelease(watcher
->state_ipv4_key
);
1184 watcher
->state_ipv4_key
= NULL
;
1186 if (watcher
->setup_ipv4_key
!= NULL
) {
1187 CFRelease(watcher
->setup_ipv4_key
);
1188 watcher
->setup_ipv4_key
= NULL
;
1194 /* global service watcher instance */
1195 static ServiceWatcherRef S_watcher
;
1199 prime_NetworkIdentification()
1201 if (S_NetworkIdentification_disabled
) {
1204 S_watcher
= ServiceWatcherCreate();
1205 ServiceWatcherUpdate(S_watcher
, TRUE
);
1210 load_NetworkIdentification(CFBundleRef bundle
, Boolean bundleVerbose
)
1212 if (bundleVerbose
) {
1213 S_NetworkIdentification_debug
= 1;
1218 #ifdef TEST_NETWORKIDENTIFICATION
1219 #undef TEST_NETWORKIDENTIFICATION
1222 main(int argc
, char **argv
)
1225 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1227 load_NetworkIdentification(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
1228 prime_NetworkIdentification();