]> git.saurik.com Git - apple/configd.git/blob - Plugins/NetworkIdentification/NetworkIdentification.c
configd-210.tar.gz
[apple/configd.git] / Plugins / NetworkIdentification / NetworkIdentification.c
1 /*
2 * Copyright (c) 2005-2007 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 * 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
28 */
29
30 /*
31 * Modification History
32 *
33 * November 9, 2006 Dieter Siegmund (dieter@apple.com)
34 * - created
35 */
36
37 #include <notify.h>
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>
44
45 /* debug output on/off */
46 static Boolean S_NetworkIdentification_debug;
47
48 /* should we bother keeping track of networks? */
49 static Boolean S_NetworkIdentification_disabled;
50
51 typedef struct ServiceWatcher_s ServiceWatcher, * ServiceWatcherRef;
52
53 /* returns an array of currently available information */
54 static CFArrayRef
55 ServiceWatcherCopyCurrent(ServiceWatcherRef watcher);
56
57 static ServiceWatcherRef
58 ServiceWatcherCreate();
59
60 static void
61 ServiceWatcherFree(ServiceWatcherRef * watcher_p);
62
63 /* XXX these should be made tunable */
64 #define SIGNATURE_HISTORY_MAX 150
65 #define SERVICE_HISTORY_MAX 5
66
67 /* don't re-write the prefs file unless this time interval has elapsed */
68 #define SIGNATURE_UPDATE_INTERVAL_SECS (24 * 3600) /* 24 hours */
69
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;
78 };
79
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
89
90 static CFArrayRef
91 make_service_entity_pattern_array(CFStringRef * keys, int n_keys)
92 {
93 int i;
94 CFArrayRef list;
95
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,
100 kSCCompAnyRegex,
101 keys[i]);
102 }
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 */
107 CFRelease(keys[i]);
108 }
109 return (list);
110 }
111
112 static CFArrayRef
113 ServiceWatcherNotificationPatterns(void)
114 {
115 CFStringRef keys[1] = { kSCEntNetIPv4 };
116
117 return (make_service_entity_pattern_array(keys,
118 sizeof(keys) / sizeof(keys[0])));
119 }
120
121 static CFArrayRef
122 ServiceWatcherPatterns(void)
123 {
124 CFStringRef keys[2] = { kSCEntNetIPv4, kSCEntNetDNS };
125
126 return (make_service_entity_pattern_array(keys,
127 sizeof(keys) / sizeof(keys[0])));
128 }
129
130 static CFTypeRef
131 myCFDictionaryArrayGetValue(CFArrayRef array, CFStringRef key, CFTypeRef value,
132 int * ret_index)
133 {
134 int count = 0;
135 int i;
136
137 if (array != NULL) {
138 count = CFArrayGetCount(array);
139 }
140 if (count == 0) {
141 goto done;
142 }
143 for (i = 0; i < count; i++) {
144 CFDictionaryRef dict;
145 CFTypeRef this_val;
146
147 dict = CFArrayGetValueAtIndex(array, i);
148 if (isA_CFDictionary(dict) == NULL) {
149 continue;
150 }
151 this_val = CFDictionaryGetValue(dict, key);
152 if (CFEqual(this_val, value)) {
153 if (ret_index != NULL) {
154 *ret_index = i;
155 }
156 return (dict);
157 }
158 }
159 done:
160 if (ret_index != NULL) {
161 *ret_index = -1;
162 }
163 return (NULL);
164 }
165
166 static CFDictionaryRef
167 copy_airport_dict(SCDynamicStoreRef store, CFStringRef if_name)
168 {
169 CFDictionaryRef dict;
170 CFStringRef key;
171
172 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
173 kSCDynamicStoreDomainState,
174 if_name,
175 kSCEntNetAirPort);
176 dict = SCDynamicStoreCopyValue(store, key);
177 CFRelease(key);
178 return (dict);
179 }
180
181 static void
182 add_airport_info(SCDynamicStoreRef store, CFMutableDictionaryRef dict)
183 {
184 CFDictionaryRef airport_dict = NULL;
185 CFStringRef key;
186 CFStringRef if_name;
187 CFDictionaryRef simple_dict;
188 CFStringRef value;
189
190 if_name = CFDictionaryGetValue(dict, kSCPropInterfaceName);
191 if (isA_CFString(if_name) == NULL) {
192 goto done;
193 }
194 airport_dict = copy_airport_dict(store, if_name);
195 if (airport_dict == NULL) {
196 goto done;
197 }
198 key = CFSTR("SSID");
199 value = CFDictionaryGetValue(airport_dict, key);
200 if (value == NULL) {
201 goto done;
202 }
203 simple_dict =
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);
210
211 done:
212 if (airport_dict != NULL) {
213 CFRelease(airport_dict);
214 }
215 return;
216 }
217
218 static CFDictionaryRef
219 get_current_dict(CFDictionaryRef current, CFStringRef entity,
220 CFArrayRef components)
221 {
222 CFDictionaryRef dict;
223 CFStringRef key;
224
225 if (CFArrayGetCount(components) < 5) {
226 /* this can't happen, we already checked */
227 return (NULL);
228 }
229 key = CFStringCreateWithFormat(NULL, NULL,
230 CFSTR("%@/%@/%@/%@/%@"),
231 CFArrayGetValueAtIndex(components, 0),
232 CFArrayGetValueAtIndex(components, 1),
233 CFArrayGetValueAtIndex(components, 2),
234 CFArrayGetValueAtIndex(components, 3),
235 entity);
236 dict = CFDictionaryGetValue(current, key);
237 CFRelease(key);
238 return (isA_CFDictionary(dict));
239 }
240
241 static CFArrayRef
242 process_dict(SCDynamicStoreRef store, CFDictionaryRef current)
243 {
244 CFMutableArrayRef array = NULL;
245 int count = 0;
246 int i;
247 const void * * keys = NULL;
248 const void * * values = NULL;
249
250 count = CFDictionaryGetCount(current);
251 if (count == 0) {
252 goto done;
253 }
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;
261 CFStringRef entity;
262 CFMutableDictionaryRef entity_dict = NULL;
263 CFMutableDictionaryRef new_dict = NULL;
264 CFStringRef sig_str = NULL;
265 CFMutableDictionaryRef service_dict = NULL;
266 CFStringRef serviceID;
267
268 if (isA_CFDictionary(values[i]) == NULL) {
269 goto loop_done;
270 }
271 components = CFStringCreateArrayBySeparatingStrings(NULL, keys[i],
272 CFSTR("/"));
273 if (components == NULL) {
274 goto loop_done;
275 }
276 if (CFArrayGetCount(components) < 5) {
277 /* too few components */
278 goto loop_done;
279 }
280 entity = CFArrayGetValueAtIndex(components, 4);
281 if (!CFEqual(entity, kSCEntNetIPv4)) {
282 goto loop_done;
283 }
284 serviceID = CFArrayGetValueAtIndex(components, 3);
285 sig_str = CFDictionaryGetValue(values[i], kNetworkSignature);
286 if (isA_CFString(sig_str) == NULL
287 || CFStringGetLength(sig_str) == 0) {
288 goto loop_done;
289 }
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);
306 }
307 CFDictionarySetValue(new_dict, kService, service_dict);
308 CFArrayAppendValue(array, new_dict);
309
310 loop_done:
311 if (entity_dict != NULL) {
312 CFRelease(entity_dict);
313 }
314 if (service_dict != NULL) {
315 CFRelease(service_dict);
316 }
317 if (components != NULL) {
318 CFRelease(components);
319 }
320 if (new_dict != NULL) {
321 CFRelease(new_dict);
322 }
323 }
324 count = CFArrayGetCount(array);
325 if (count == 0) {
326 CFRelease(array);
327 array = NULL;
328 goto done;
329 }
330
331 done:
332 if (keys != NULL) {
333 free(keys);
334 }
335 if (values != NULL) {
336 free(values);
337 }
338 return (array);
339
340 }
341
342 static CFArrayRef
343 ServiceWatcherCopyCurrent(ServiceWatcherRef watcher)
344 {
345 CFDictionaryRef current;
346 CFArrayRef list;
347 CFArrayRef ret = NULL;
348
349 list = ServiceWatcherPatterns();
350 current = SCDynamicStoreCopyMultiple(watcher->store, NULL, list);
351 CFRelease(list);
352 if (current == NULL) {
353 goto done;
354 }
355 ret = process_dict(watcher->store, current);
356 done:
357 if (current != NULL) {
358 CFRelease(current);
359 }
360 return (ret);
361 }
362
363 static Boolean
364 ServiceWatcherSetActiveSignatures(ServiceWatcherRef watcher, CFArrayRef active)
365 {
366 Boolean changed = FALSE;
367 CFArrayRef prev_active;
368
369 prev_active = watcher->active_signatures;
370 if (prev_active == NULL && active == NULL) {
371 /* nothing to do */
372 goto done;
373 }
374 if (prev_active != NULL && active != NULL) {
375 changed = !CFEqual(prev_active, active);
376 }
377 else {
378 changed = TRUE;
379 }
380 if (active != NULL) {
381 CFRetain(active);
382 }
383 if (prev_active != NULL) {
384 CFRelease(prev_active);
385 }
386 watcher->active_signatures = active;
387 if (changed) {
388 if (active != NULL) {
389 SCLog(S_NetworkIdentification_debug,
390 LOG_NOTICE, CFSTR("Active Signatures %@"), active);
391 }
392 else {
393 SCLog(S_NetworkIdentification_debug,
394 LOG_NOTICE, CFSTR("No Active Signatures"));
395 }
396 }
397 done:
398 return (changed);
399 }
400
401 static Boolean
402 ServiceWatcherSetPrimaryIPv4(ServiceWatcherRef watcher,
403 CFStringRef primary_ipv4)
404 {
405 Boolean changed = FALSE;
406 CFStringRef prev_ipv4_primary;
407
408 prev_ipv4_primary = watcher->primary_ipv4;
409 if (prev_ipv4_primary == NULL && primary_ipv4 == NULL) {
410 /* nothing to do */
411 goto done;
412 }
413 if (prev_ipv4_primary != NULL && primary_ipv4 != NULL) {
414 changed = !CFEqual(prev_ipv4_primary, primary_ipv4);
415 }
416 else {
417 changed = TRUE;
418 }
419 if (primary_ipv4 != NULL) {
420 CFRetain(primary_ipv4);
421 }
422 if (prev_ipv4_primary != NULL) {
423 CFRelease(prev_ipv4_primary);
424 }
425 watcher->primary_ipv4 = primary_ipv4;
426 if (changed) {
427 if (primary_ipv4 != NULL) {
428 SCLog(S_NetworkIdentification_debug,
429 LOG_NOTICE, CFSTR("Primary IPv4 %@"), primary_ipv4);
430 }
431 else {
432 SCLog(S_NetworkIdentification_debug, LOG_NOTICE,
433 CFSTR("No Primary IPv4"));
434 }
435 }
436 done:
437 return (changed);
438 }
439
440
441 static CFDictionaryRef
442 signature_add_service(CFDictionaryRef sig_dict, CFDictionaryRef service,
443 CFArrayRef active_services)
444 {
445 CFArrayRef list;
446 CFMutableDictionaryRef new_dict = NULL;
447 CFDateRef now;
448
449 list = CFDictionaryGetValue(sig_dict, kServices);
450 now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
451 if (list == NULL) {
452 list = CFArrayCreate(NULL, (const void * *)&service, 1,
453 &kCFTypeArrayCallBacks);
454 }
455 else {
456 int list_count = CFArrayGetCount(list);
457 CFMutableArrayRef new_list = NULL;
458 CFRange range = CFRangeMake(0, list_count);
459 int where;
460
461 where = CFArrayGetFirstIndexOfValue(list, range, service);
462 if (where != kCFNotFound) {
463 CFDateRef date;
464
465 date = CFDictionaryGetValue(sig_dict, kTimestamp);
466 if (date != NULL) {
467 CFTimeInterval time_interval;
468
469 time_interval = CFDateGetTimeIntervalSinceDate(now, date);
470 /* don't bother updating timestamp until interval has passed */
471 if (time_interval < (SIGNATURE_UPDATE_INTERVAL_SECS)) {
472 goto done;
473 }
474 }
475 if (where == 0) {
476 /* it's already in the right place */
477 list = NULL;
478 }
479 }
480
481 if (list != NULL) {
482 new_list = CFArrayCreateMutableCopy(NULL, 0, list);
483 if (where != kCFNotFound) {
484 CFArrayRemoveValueAtIndex(new_list, where);
485 }
486 else {
487 list_count++;
488 }
489 CFArrayInsertValueAtIndex(new_list, 0, service);
490 /* try to remove stale entries */
491 if (list_count > SERVICE_HISTORY_MAX) {
492 int i;
493 int remove_count = list_count - SERVICE_HISTORY_MAX;
494
495 SCLog(S_NetworkIdentification_debug,
496 LOG_NOTICE, CFSTR("Attempting to remove %d services"),
497 remove_count);
498 for (i = list_count - 1; i >= 0 && remove_count > 0; i--) {
499 CFDictionaryRef dict;
500
501 dict = CFArrayGetValueAtIndex(new_list, i);
502 if (myCFDictionaryArrayGetValue(active_services,
503 kService, dict, NULL)
504 != NULL) {
505 /* skip anything that's currently active */
506 SCLog(S_NetworkIdentification_debug,
507 LOG_NOTICE, CFSTR("Skipping Service %@"),
508 dict);
509 }
510 else {
511 SCLog(S_NetworkIdentification_debug, LOG_NOTICE,
512 CFSTR("Removing Service %@"), dict);
513 CFArrayRemoveValueAtIndex(new_list, i);
514 remove_count--;
515 }
516 }
517 }
518 list = (CFArrayRef)new_list;
519
520 }
521 }
522
523 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, sig_dict);
524 if (list != NULL) {
525 CFDictionarySetValue(new_dict, kServices, list);
526 CFRelease(list);
527 }
528 CFDictionarySetValue(new_dict, kTimestamp, now);
529
530 done:
531 CFRelease(now);
532 return (new_dict);
533 }
534
535 #define ARBITRARILY_LARGE_NUMBER (1024 * 1024)
536 static CFStringRef
537 get_best_serviceID(CFArrayRef serviceID_list, CFArrayRef order)
538 {
539 int best_rank;
540 CFStringRef best_serviceID;
541 int count;
542 int i;
543 CFRange range;
544
545 count = CFArrayGetCount(serviceID_list);
546 if (count == 1 || order == NULL) {
547 return (CFArrayGetValueAtIndex(serviceID_list, 0));
548 }
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);
554 int this_rank;
555
556 this_rank = CFArrayGetFirstIndexOfValue(order, range, serviceID);
557 if (this_rank == kCFNotFound) {
558 this_rank = ARBITRARILY_LARGE_NUMBER;
559 }
560 if (best_serviceID == NULL || this_rank < best_rank) {
561 best_serviceID = serviceID;
562 best_rank = this_rank;
563 }
564 }
565 return (best_serviceID);
566 }
567
568 static CFArrayRef
569 copy_service_order(SCDynamicStoreRef session, CFStringRef ipv4_key)
570 {
571 CFArrayRef order = NULL;
572 CFDictionaryRef ipv4_dict = NULL;
573
574 if (session == NULL) {
575 return (NULL);
576 }
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);
581 if (order) {
582 CFRetain(order);
583 }
584 }
585 if (ipv4_dict != NULL) {
586 CFRelease(ipv4_dict);
587 }
588 return (order);
589 }
590
591 typedef struct service_order_with_range {
592 CFArrayRef service_order;
593 CFRange range;
594 } service_order_with_range_t;
595
596 static void
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)
600
601 {
602 int i;
603 int serviceID_index;
604
605 if (count == 0 || order->service_order == NULL) {
606 goto add_to_end;
607 }
608 serviceID_index = CFArrayGetFirstIndexOfValue(order->service_order,
609 order->range,
610 serviceID);
611 if (serviceID_index == kCFNotFound) {
612 goto add_to_end;
613 }
614 for (i = 0; i < count; i++) {
615 CFStringRef scan = CFArrayGetValueAtIndex(serviceID_list, i);
616 int scan_index;
617
618 scan_index = CFArrayGetFirstIndexOfValue(order->service_order,
619 order->range,
620 scan);
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);
626 return;
627 }
628 }
629
630 add_to_end:
631 CFArrayAppendValue(netID_list, netID);
632 CFArrayAppendValue(serviceID_list, serviceID);
633 return;
634 }
635
636 static Boolean
637 ServiceWatcherPublishActiveIdentifiers(ServiceWatcherRef watcher)
638 {
639 Boolean updated = FALSE;
640
641 if (watcher->active_signatures == NULL) {
642 CFDictionaryRef dict;
643
644 dict = SCDynamicStoreCopyValue(watcher->store,
645 kSCNetworkIdentificationStoreKey);
646 if (dict != NULL) {
647 updated = TRUE;
648 SCLog(S_NetworkIdentification_debug,
649 LOG_NOTICE, CFSTR("Removing %@"),
650 kSCNetworkIdentificationStoreKey);
651 SCDynamicStoreRemoveValue(watcher->store,
652 kSCNetworkIdentificationStoreKey);
653 CFRelease(dict);
654 }
655 }
656 else {
657 int count;
658 CFDictionaryRef dict;
659 int i;
660 CFMutableArrayRef id_list;
661 CFStringRef keys[3];
662 int keys_count;
663 service_order_with_range_t order;
664 CFStringRef primary_ipv4_id = NULL;
665 CFMutableArrayRef serviceID_list;
666 CFDictionaryRef store_dict;
667 CFTypeRef values[3];
668
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));
673 }
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++) {
678 CFStringRef this_id;
679 CFStringRef serviceID;
680 CFArrayRef this_list;
681
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) {
686 CFRange range;
687
688 range = CFRangeMake(0, CFArrayGetCount(this_list));
689 if (CFArrayContainsValue(this_list, range,
690 watcher->primary_ipv4)) {
691 primary_ipv4_id = this_id;
692 }
693 }
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);
697 }
698 keys[0] = kStoreKeyActiveIdentifiers;
699 values[0] = id_list;
700 keys[1] = kStoreKeyServiceIdentifiers;
701 values[1] = serviceID_list;
702 if (primary_ipv4_id != NULL) {
703 keys_count = 3;
704 keys[2] = kStoreKeyPrimaryIPv4Identifier;
705 values[2] = primary_ipv4_id;
706 }
707 else {
708 keys_count = 2;
709 }
710 dict = CFDictionaryCreate(NULL, (const void * *)keys,
711 (const void * *)values, keys_count,
712 &kCFTypeDictionaryKeyCallBacks,
713 &kCFTypeDictionaryValueCallBacks);
714 store_dict
715 = SCDynamicStoreCopyValue(watcher->store,
716 kSCNetworkIdentificationStoreKey);
717 if (isA_CFDictionary(store_dict) == NULL
718 || CFEqual(store_dict, dict) == FALSE) {
719 updated = TRUE;
720 SCDynamicStoreSetValue(watcher->store,
721 kSCNetworkIdentificationStoreKey, dict);
722 SCLog(S_NetworkIdentification_debug,
723 LOG_NOTICE, CFSTR("Setting %@ = %@"),
724 kSCNetworkIdentificationStoreKey,
725 dict);
726 }
727 else {
728 SCLog(S_NetworkIdentification_debug,
729 LOG_NOTICE, CFSTR("Not setting %@"),
730 kSCNetworkIdentificationStoreKey);
731 }
732 CFRelease(dict);
733 CFRelease(id_list);
734 CFRelease(serviceID_list);
735 if (order.service_order != NULL) {
736 CFRelease(order.service_order);
737 }
738 if (store_dict != NULL) {
739 CFRelease(store_dict);
740 }
741 }
742 return (updated);
743 }
744
745 static CFDictionaryRef
746 signature_dict_create(CFStringRef this_sig, CFDictionaryRef service)
747 {
748 CFDictionaryRef dict;
749 const void * keys[4];
750 const void * values[4];
751
752 keys[0] = kSignature;
753 values[0] = this_sig;
754
755 keys[1] = kServices;
756 values[1] = CFArrayCreate(NULL, (const void * *)&service, 1,
757 &kCFTypeArrayCallBacks);
758 keys[2] = kIdentifier;
759 values[2] = this_sig;
760
761 keys[3] = kTimestamp;
762 values[3] = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
763
764 dict = CFDictionaryCreate(NULL, keys, values,
765 sizeof(keys) / sizeof(keys[0]),
766 &kCFTypeDictionaryKeyCallBacks,
767 &kCFTypeDictionaryValueCallBacks);
768 CFRelease(values[1]);
769 CFRelease(values[3]);
770 return (dict);
771 }
772
773 static void
774 ServiceWatcherRemoveStaleSignatures(ServiceWatcherRef watcher)
775 {
776 int active_count = 0;
777 int count;
778 int i;
779 int remove_count;
780
781 count = CFArrayGetCount(watcher->signatures);
782 if (watcher->active_signatures != NULL) {
783 active_count = CFArrayGetCount(watcher->active_signatures);
784 }
785 if ((count - active_count) <= SIGNATURE_HISTORY_MAX) {
786 return;
787 }
788 remove_count = count - active_count - SIGNATURE_HISTORY_MAX;
789 for (i = count - 1; i >= 0 && remove_count > 0; i--) {
790 CFDictionaryRef sig_dict;
791 CFStringRef sig_str;
792
793 sig_dict = CFArrayGetValueAtIndex(watcher->signatures, i);
794 sig_str = CFDictionaryGetValue(sig_dict, kSignature);
795
796 if (myCFDictionaryArrayGetValue(watcher->active_signatures,
797 kSignature, sig_str, NULL)
798 != NULL) {
799 /* skip anything that's currently active */
800 SCLog(S_NetworkIdentification_debug,
801 LOG_NOTICE, CFSTR("Skipping %@"), sig_dict);
802 }
803 else {
804 SCLog(S_NetworkIdentification_debug,
805 LOG_NOTICE, CFSTR("ServiceWatcher: Removing %@"),
806 sig_dict);
807 CFArrayRemoveValueAtIndex(watcher->signatures, i);
808 remove_count--;
809 }
810 }
811 return;
812 }
813
814 static void
815 ServiceWatcherSaveSignatures(ServiceWatcherRef watcher)
816 {
817 SCPreferencesRef prefs;
818
819 prefs = SCPreferencesCreate(NULL, CFSTR("ServiceWatcher"),
820 kSCNetworkIdentificationPrefsKey);
821 if (prefs == NULL) {
822 SCLog(TRUE, LOG_NOTICE, CFSTR("ServiceWatcherSaveSignatures: Create failed %s"),
823 SCErrorString(SCError()));
824 return;
825 }
826 ServiceWatcherRemoveStaleSignatures(watcher);
827 if (SCPreferencesSetValue(prefs, kSignatures, watcher->signatures)
828 == FALSE) {
829 SCLog(TRUE, LOG_NOTICE, CFSTR("ServiceWatcherSaveSignatures: Set failed %s"),
830 SCErrorString(SCError()));
831 }
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()));
838 }
839 }
840 CFRelease(prefs);
841 return;
842
843 }
844
845 static void
846 ServiceWatcherLoadSignatures(ServiceWatcherRef watcher)
847 {
848 int count;
849 int i;
850 SCPreferencesRef prefs;
851 CFArrayRef signatures;
852
853 watcher->signatures
854 = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
855 prefs = SCPreferencesCreate(NULL, CFSTR("ServiceWatcher"),
856 kSCNetworkIdentificationPrefsKey);
857 if (prefs == NULL) {
858 SCLog(TRUE, LOG_NOTICE, CFSTR("ServiceWatcherLoadSignatures: Create failed %s"),
859 SCErrorString(SCError()));
860 return;
861 }
862 signatures = SCPreferencesGetValue(prefs, kSignatures);
863 if (signatures == NULL) {
864 goto done;
865 }
866 if (isA_CFArray(signatures) == NULL) {
867 SCLog(TRUE, LOG_NOTICE,
868 CFSTR("ServiceWatcherLoadSignatures: Signatures is not an array"));
869 goto done;
870 }
871 count = CFArrayGetCount(signatures);
872 for (i = 0; i < count; i++) {
873 CFDictionaryRef dict;
874 CFArrayRef services;
875 CFStringRef sig_id;
876 CFStringRef sig_str;
877 CFDateRef timestamp;
878
879 dict = CFArrayGetValueAtIndex(signatures, i);
880 if (isA_CFDictionary(dict) == NULL) {
881 continue;
882 }
883 sig_id = CFDictionaryGetValue(dict, kIdentifier);
884 if (isA_CFString(sig_id) == NULL) {
885 continue;
886 }
887 sig_str = CFDictionaryGetValue(dict, kSignature);
888 if (isA_CFString(sig_str) == NULL) {
889 continue;
890 }
891 timestamp = CFDictionaryGetValue(dict, kTimestamp);
892 if (isA_CFDate(timestamp) == NULL) {
893 continue;
894 }
895 services = CFDictionaryGetValue(dict, kServices);
896 if (isA_CFArray(services) == NULL) {
897 continue;
898 }
899 CFArrayAppendValue(watcher->signatures, dict);
900 }
901
902 done:
903 CFRelease(prefs);
904 return;
905
906 }
907
908 static void
909 ServiceWatcherUpdate(ServiceWatcherRef watcher, Boolean update_signatures)
910 {
911 CFMutableArrayRef active_signatures = NULL;
912 int count;
913 int i;
914 Boolean save_signatures = FALSE;
915 CFArrayRef service_list;
916 Boolean update_store = FALSE;
917
918 service_list = ServiceWatcherCopyCurrent(watcher);
919 SCLog(S_NetworkIdentification_debug,
920 LOG_NOTICE, CFSTR("service_list = %@"), service_list);
921 if (service_list == NULL) {
922 goto done;
923 }
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;
929 CFArrayRef id_list;
930 CFMutableDictionaryRef new_active_dict;
931 CFDictionaryRef new_sig_dict;
932 CFStringRef serviceID;
933 CFStringRef sig_id;
934 CFDictionaryRef service;
935 CFDictionaryRef sig_dict;
936 CFStringRef this_sig;
937 int where;
938
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 */
944 continue;
945 }
946 sig_dict = myCFDictionaryArrayGetValue(watcher->signatures, kSignature,
947 this_sig, &where);
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);
952 CFRelease(sig_dict);
953 save_signatures = TRUE;
954 sig_id = CFDictionaryGetValue(sig_dict, kIdentifier);
955 active_dict = NULL;
956 }
957 else {
958 /* update an existing signature entry */
959
960 sig_id = CFDictionaryGetValue(sig_dict, kIdentifier);
961 new_sig_dict = signature_add_service(sig_dict, service,
962 service_list);
963 if (new_sig_dict != NULL) {
964 CFArrayRemoveValueAtIndex(watcher->signatures, where);
965 CFArrayInsertValueAtIndex(watcher->signatures, 0,
966 new_sig_dict);
967 CFRelease(new_sig_dict);
968 sig_dict = new_sig_dict;
969 save_signatures = TRUE;
970 }
971 active_dict
972 = myCFDictionaryArrayGetValue(active_signatures,
973 kSignature, this_sig,
974 &where);
975 }
976 if (active_dict == NULL) {
977 /* signature now active, this is the first/only service */
978 new_active_dict
979 = CFDictionaryCreateMutable(NULL, 0,
980 &kCFTypeDictionaryKeyCallBacks,
981 &kCFTypeDictionaryValueCallBacks);
982 CFDictionarySetValue(new_active_dict, kSignature, this_sig);
983 CFDictionarySetValue(new_active_dict, kIdentifier, sig_id);
984 serviceID = CFDictionaryGetValue(service, kServiceID);
985 id_list = CFArrayCreate(NULL, (const void * *)&serviceID, 1,
986 &kCFTypeArrayCallBacks);
987 CFDictionarySetValue(new_active_dict, kServiceIdentifiers,
988 id_list);
989 CFArrayAppendValue(active_signatures, new_active_dict);
990 CFRelease(new_active_dict);
991 CFRelease(id_list);
992 }
993 else {
994 /* signature already active, add this serviceID */
995 CFRange range;
996
997 id_list = CFDictionaryGetValue(active_dict,
998 kServiceIdentifiers);
999 range = CFRangeMake(0, CFArrayGetCount(id_list));
1000 serviceID = CFDictionaryGetValue(service, kServiceID);
1001 if (CFArrayContainsValue(id_list, range, serviceID) == FALSE) {
1002 CFMutableDictionaryRef new_active_dict;
1003 CFMutableArrayRef new_id_list;
1004
1005 new_id_list = CFArrayCreateMutableCopy(NULL, 0, id_list);
1006 CFArrayAppendValue(new_id_list, serviceID);
1007 new_active_dict
1008 = CFDictionaryCreateMutableCopy(NULL, 0, active_dict);
1009 CFDictionarySetValue(new_active_dict, kServiceIdentifiers,
1010 new_id_list);
1011 CFArraySetValueAtIndex(active_signatures, where,
1012 new_active_dict);
1013 CFRelease(new_active_dict);
1014 CFRelease(new_id_list);
1015 }
1016 }
1017 }
1018 done:
1019 if (active_signatures == NULL
1020 || CFArrayGetCount(active_signatures) == 0) {
1021 update_store
1022 = ServiceWatcherSetActiveSignatures(watcher, NULL);
1023 }
1024 else {
1025 update_store
1026 = ServiceWatcherSetActiveSignatures(watcher, active_signatures);
1027 }
1028 if (save_signatures) {
1029 /* write out the file */
1030 ServiceWatcherSaveSignatures(watcher);
1031 }
1032
1033 if (service_list != NULL) {
1034 CFRelease(service_list);
1035 }
1036 if (active_signatures != NULL) {
1037 CFRelease(active_signatures);
1038 }
1039 if (update_signatures || update_store) {
1040 if (ServiceWatcherPublishActiveIdentifiers(watcher)) {
1041 notify_post(kSCNetworkSignatureActiveChangedNotifyName);
1042 }
1043 }
1044 return;
1045 }
1046
1047 static Boolean
1048 update_primary_ipv4(ServiceWatcherRef watcher)
1049 {
1050 Boolean changed = FALSE;
1051 CFDictionaryRef global_ipv4;
1052
1053 global_ipv4 = SCDynamicStoreCopyValue(watcher->store,
1054 watcher->state_ipv4_key);
1055 if (isA_CFDictionary(global_ipv4) != NULL) {
1056 CFStringRef primary_ipv4;
1057
1058 primary_ipv4
1059 = CFDictionaryGetValue(global_ipv4,
1060 kSCDynamicStorePropNetPrimaryService);
1061 changed = ServiceWatcherSetPrimaryIPv4(watcher,
1062 isA_CFString(primary_ipv4));
1063 }
1064 if (global_ipv4 != NULL) {
1065 CFRelease(global_ipv4);
1066 }
1067 return (changed);
1068 }
1069
1070 static void
1071 ServiceWatcherNotifier(SCDynamicStoreRef not_used, CFArrayRef changes,
1072 void * info)
1073 {
1074 int count;
1075 int i;
1076 Boolean order_changed = FALSE;
1077 Boolean global_ipv4_changed = FALSE;
1078 Boolean primary_ipv4_changed = FALSE;
1079 ServiceWatcherRef watcher = (ServiceWatcherRef)info;
1080
1081 count = CFArrayGetCount(changes);
1082 if (count == 0) {
1083 return;
1084 }
1085 for (i = 0; i < count; i++) {
1086 CFStringRef key = CFArrayGetValueAtIndex(changes, i);
1087
1088 if (CFStringHasPrefix(key, kSCDynamicStoreDomainSetup)) {
1089 order_changed = TRUE;
1090 }
1091 else if (CFEqual(key, watcher->state_ipv4_key)) {
1092 global_ipv4_changed = TRUE;
1093 }
1094 }
1095 if (global_ipv4_changed) {
1096 primary_ipv4_changed = update_primary_ipv4(watcher);
1097 }
1098 if (count == 1
1099 && (order_changed || primary_ipv4_changed)) {
1100 /* just the service order or the primary service changed */
1101 if (ServiceWatcherPublishActiveIdentifiers(watcher)) {
1102 notify_post(kSCNetworkSignatureActiveChangedNotifyName);
1103 }
1104 }
1105 else {
1106 ServiceWatcherUpdate(watcher, order_changed || primary_ipv4_changed);
1107 }
1108 return;
1109 }
1110
1111 static ServiceWatcherRef
1112 ServiceWatcherCreate()
1113 {
1114 SCDynamicStoreContext context = { 0, 0, 0, 0, 0};
1115 CFArrayRef patterns;
1116 CFStringRef keys[2];
1117 CFArrayRef key_list;
1118 ServiceWatcherRef watcher;
1119
1120 watcher = malloc(sizeof(*watcher));
1121 bzero(watcher, sizeof(*watcher));
1122 context.info = watcher;
1123 watcher->store = SCDynamicStoreCreate(NULL, CFSTR("Service Watcher"),
1124 ServiceWatcherNotifier, &context);
1125 if (watcher->store == NULL) {
1126 SCLog(TRUE, LOG_NOTICE, CFSTR("SCDynamicStoreCreate failed: %s"),
1127 SCErrorString(SCError()));
1128 goto failed;
1129 }
1130 watcher->setup_ipv4_key
1131 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
1132 kSCDynamicStoreDomainSetup,
1133 kSCEntNetIPv4);
1134 watcher->state_ipv4_key
1135 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
1136 kSCDynamicStoreDomainState,
1137 kSCEntNetIPv4);
1138 keys[0] = watcher->setup_ipv4_key;
1139 keys[1] = watcher->state_ipv4_key;
1140 key_list = CFArrayCreate(NULL, (const void * *)keys, sizeof(keys) / sizeof(keys[0]),
1141 &kCFTypeArrayCallBacks);
1142 patterns = ServiceWatcherNotificationPatterns();
1143 (void)SCDynamicStoreSetNotificationKeys(watcher->store, key_list, patterns);
1144 CFRelease(patterns);
1145 CFRelease(key_list);
1146 watcher->rls = SCDynamicStoreCreateRunLoopSource(NULL, watcher->store, 0);
1147 CFRunLoopAddSource(CFRunLoopGetCurrent(), watcher->rls,
1148 kCFRunLoopDefaultMode);
1149 ServiceWatcherLoadSignatures(watcher);
1150 update_primary_ipv4(watcher);
1151 return (watcher);
1152 failed:
1153 ServiceWatcherFree(&watcher);
1154 return (NULL);
1155 }
1156
1157 void
1158 ServiceWatcherFree(ServiceWatcherRef * watcher_p)
1159 {
1160 ServiceWatcherRef watcher;
1161
1162 if (watcher_p == NULL) {
1163 return;
1164 }
1165 watcher = *watcher_p;
1166 if (watcher == NULL) {
1167 return;
1168 }
1169 *watcher_p = NULL;
1170 if (watcher->store != NULL) {
1171 CFRelease(watcher->store);
1172 watcher->store = NULL;
1173 }
1174 if (watcher->rls != NULL) {
1175 CFRunLoopSourceInvalidate(watcher->rls);
1176 CFRelease(watcher->rls);
1177 watcher->rls = NULL;
1178 }
1179 if (watcher->signatures != NULL) {
1180 CFRelease(watcher->signatures);
1181 watcher->signatures = NULL;
1182 }
1183 if (watcher->state_ipv4_key != NULL) {
1184 CFRelease(watcher->state_ipv4_key);
1185 watcher->state_ipv4_key = NULL;
1186 }
1187 if (watcher->setup_ipv4_key != NULL) {
1188 CFRelease(watcher->setup_ipv4_key);
1189 watcher->setup_ipv4_key = NULL;
1190 }
1191 free(watcher);
1192 return;
1193 }
1194
1195 /* global service watcher instance */
1196 static ServiceWatcherRef S_watcher;
1197
1198 __private_extern__
1199 void
1200 prime_NetworkIdentification()
1201 {
1202 if (S_NetworkIdentification_disabled) {
1203 return;
1204 }
1205 S_watcher = ServiceWatcherCreate();
1206 ServiceWatcherUpdate(S_watcher, TRUE);
1207 }
1208
1209 __private_extern__
1210 void
1211 load_NetworkIdentification(CFBundleRef bundle, Boolean bundleVerbose)
1212 {
1213 if (bundleVerbose) {
1214 S_NetworkIdentification_debug = 1;
1215 }
1216 return;
1217 }
1218
1219 __private_extern__
1220 void
1221 stop_NetworkIdentification(CFRunLoopSourceRef stopRls)
1222 {
1223 if (S_watcher != NULL) {
1224 ServiceWatcherSaveSignatures(S_watcher);
1225 }
1226 CFRunLoopSourceSignal(stopRls);
1227 }
1228
1229
1230 #ifdef TEST_NETWORKIDENTIFICATION
1231 #undef TEST_NETWORKIDENTIFICATION
1232
1233 int
1234 main(int argc, char **argv)
1235 {
1236 _sc_log = FALSE;
1237 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1238
1239 load_NetworkIdentification(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
1240 prime_NetworkIdentification();
1241 CFRunLoopRun();
1242 /* not reached */
1243 exit(0);
1244 return 0;
1245 }
1246 #endif
1247