2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
24 * Modification History
26 * June 10, 2001 Allan Nathanson <ajn@apple.com>
27 * - updated to use service-based "State:" information
29 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
32 * January 30, 2001 Allan Nathanson <ajn@apple.com>
36 #include <SystemConfiguration/SystemConfiguration.h>
37 #include <SystemConfiguration/SCPrivate.h>
38 #include <SystemConfiguration/SCValidation.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <arpa/nameser.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
53 inet_atonCF(CFStringRef cfStr
, struct in_addr
*addr
)
55 char cStr
[sizeof("255.255.255.255")];
57 if (!CFStringGetCString(cfStr
, cStr
, sizeof(cStr
), kCFStringEncodingMacRoman
)) {
61 return inet_aton(cStr
, addr
);
66 * Function: parse_component
68 * Given a string 'key' and a string prefix 'prefix',
69 * return the next component in the slash '/' separated
73 * 1. key = "a/b/c" prefix = "a/"
75 * 2. key = "a/b/c" prefix = "a/b/"
79 parse_component(CFStringRef key
, CFStringRef prefix
)
81 CFMutableStringRef comp
;
84 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
87 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
88 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
89 range
= CFStringFind(comp
, CFSTR("/"), 0);
90 if (range
.location
== kCFNotFound
) {
93 range
.length
= CFStringGetLength(comp
) - range
.location
;
94 CFStringDelete(comp
, range
);
100 CFMutableDictionaryRef aDict
; /* active services */
101 CFStringRef aPrefix
; /* prefix for active services */
102 CFMutableDictionaryRef cDict
; /* configured services */
103 CFStringRef cPrefix
; /* prefix for configured services */
104 CFMutableDictionaryRef iDict
; /* active interfaces */
105 CFStringRef iPrefix
; /* prefix for active interfaces */
106 CFMutableArrayRef order
; /* service order */
107 } initContext
, *initContextRef
;
111 collectInfo(const void *key
, const void *value
, void *context
)
113 initContextRef info
= (initContextRef
)context
;
114 CFStringRef interface
;
115 CFStringRef interfaceKey
;
117 CFStringRef serviceKey
;
119 if (!isA_CFString(key
) || !isA_CFDictionary(value
)) {
123 service
= parse_component((CFStringRef
)key
, info
->cPrefix
);
125 serviceKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
126 kSCDynamicStoreDomainSetup
,
129 if (CFEqual((CFStringRef
)key
, serviceKey
)) {
130 CFMutableDictionaryRef dict
;
132 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
)value
);
133 CFDictionaryAddValue(info
->cDict
, service
, dict
);
136 CFRelease(serviceKey
);
138 if (!CFArrayContainsValue(info
->order
, CFRangeMake(0, CFArrayGetCount(info
->order
)), service
)) {
139 CFArrayAppendValue(info
->order
, service
);
146 service
= parse_component((CFStringRef
)key
, info
->aPrefix
);
148 serviceKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
149 kSCDynamicStoreDomainState
,
152 if (CFEqual((CFStringRef
)key
, serviceKey
)) {
153 CFMutableDictionaryRef dict
;
155 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
)value
);
156 CFDictionaryAddValue(info
->aDict
, service
, dict
);
159 CFRelease(serviceKey
);
161 if (!CFArrayContainsValue(info
->order
, CFRangeMake(0, CFArrayGetCount(info
->order
)), service
)) {
162 CFArrayAppendValue(info
->order
, service
);
169 interface
= parse_component((CFStringRef
)key
, info
->iPrefix
);
171 interfaceKey
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
172 kSCDynamicStoreDomainState
,
175 if (CFEqual((CFStringRef
)key
, interfaceKey
)) {
176 CFMutableDictionaryRef dict
;
178 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
)value
);
179 CFDictionaryAddValue(info
->iDict
, interface
, dict
);
182 CFRelease(interfaceKey
);
183 CFRelease(interface
);
192 collectExtraInfo(const void *key
, const void *value
, void *context
)
194 CFStringRef interfaceKey
;
195 initContextRef info
= (initContextRef
)context
;
196 CFMutableDictionaryRef dict
;
201 if (!isA_CFString(key
) || !isA_CFDictionary(value
)) {
205 service
= parse_component((CFStringRef
)key
, info
->cPrefix
);
207 /* this key/value pair contains supplemental information */
211 dict
= (CFMutableDictionaryRef
)CFDictionaryGetValue(info
->cDict
, service
);
213 /* we don't have any IPv4 information for this service */
217 interfaceKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
218 kSCDynamicStoreDomainSetup
,
221 match
= CFEqual((CFStringRef
)key
, interfaceKey
);
222 CFRelease(interfaceKey
);
224 CFStringRef interface
;
226 interface
= CFDictionaryGetValue((CFDictionaryRef
)value
,
227 kSCPropNetInterfaceType
);
228 if (isA_CFString(interface
)) {
229 /* if "InterfaceType" available */
230 CFDictionaryAddValue(dict
, kSCPropNetInterfaceType
, interface
);
231 CFDictionarySetValue(info
->cDict
, service
, dict
);
236 pppKey
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
237 kSCDynamicStoreDomainSetup
,
240 match
= CFEqual((CFStringRef
)key
, pppKey
);
243 CFNumberRef dialOnDemand
;
245 dialOnDemand
= CFDictionaryGetValue((CFDictionaryRef
)value
,
246 kSCPropNetPPPDialOnDemand
);
247 if (isA_CFNumber(dialOnDemand
)) {
248 /* if "DialOnDemand" information not available */
249 CFDictionaryAddValue(dict
, kSCPropNetPPPDialOnDemand
, dialOnDemand
);
250 CFDictionarySetValue(info
->cDict
, service
, dict
);
263 removeKnownAddresses(const void *key
, const void *value
, void *context
)
265 CFMutableDictionaryRef ifDict
;
267 CFMutableDictionaryRef interfaces
= (CFMutableDictionaryRef
)context
;
268 CFMutableDictionaryRef serviceDict
= (CFMutableDictionaryRef
)value
;
269 Boolean updated
= FALSE
;
276 CFMutableArrayRef nAddrs
= NULL
;
277 CFMutableArrayRef nDests
= NULL
;
278 CFMutableArrayRef nMasks
= NULL
;
284 ifName
= CFDictionaryGetValue(serviceDict
, kSCPropInterfaceName
);
286 /* if no "InterfaceName" for this service */
290 ifDict
= (CFMutableDictionaryRef
)CFDictionaryGetValue(interfaces
, ifName
);
292 /* if the indicated interface is not active */
296 sAddrs
= isA_CFArray(CFDictionaryGetValue(serviceDict
,
297 kSCPropNetIPv4Addresses
));
298 sDests
= isA_CFArray(CFDictionaryGetValue(serviceDict
,
299 kSCPropNetIPv4DestAddresses
));
300 sMasks
= isA_CFArray(CFDictionaryGetValue(serviceDict
,
301 kSCPropNetIPv4SubnetMasks
));
303 if (!sAddrs
|| ((n
= CFArrayGetCount(sAddrs
)) == 0)) {
304 /* if no addresses */
308 if (((sMasks
== NULL
) && (sDests
== NULL
)) ||
309 ((sMasks
!= NULL
) && (sDests
!= NULL
))) {
311 * sorry, we expect to have "SubnetMasks" or
312 * "DestAddresses" (not both).
317 if (sMasks
&& (n
!= CFArrayGetCount(sMasks
))) {
318 /* if we don't like the "SubnetMasks" */
322 if (sDests
&& (n
!= CFArrayGetCount(sDests
))) {
323 /* if we don't like the "DestAddresses" */
327 iAddrs
= isA_CFArray(CFDictionaryGetValue(ifDict
,
328 kSCPropNetIPv4Addresses
));
329 iDests
= isA_CFArray(CFDictionaryGetValue(ifDict
,
330 kSCPropNetIPv4DestAddresses
));
331 iMasks
= isA_CFArray(CFDictionaryGetValue(ifDict
,
332 kSCPropNetIPv4SubnetMasks
));
334 if (((iMasks
== NULL
) && (iDests
== NULL
)) ||
335 ((iMasks
!= NULL
) && (iDests
!= NULL
))) {
337 * sorry, we expect to have "SubnetMasks" or
338 * "DestAddresses" (not both).
343 if (!iAddrs
|| ((i
= CFArrayGetCount(iAddrs
)) == 0)) {
344 /* if no addresses */
348 if (iMasks
&& (i
!= CFArrayGetCount(iMasks
))) {
349 /* if we don't like the "SubnetMasks" */
353 if (iDests
&& (i
!= CFArrayGetCount(iDests
))) {
354 /* if we don't like the "DestAddresses" */
358 if (((sMasks
== NULL
) && (iMasks
!= NULL
)) ||
359 ((sDests
== NULL
) && (iDests
!= NULL
))) {
360 /* if our addressing schemes are in conflict */
364 nAddrs
= CFArrayCreateMutableCopy(NULL
, 0, iAddrs
);
365 if (iMasks
) nMasks
= CFArrayCreateMutableCopy(NULL
, 0, iMasks
);
366 if (iDests
) nDests
= CFArrayCreateMutableCopy(NULL
, 0, iDests
);
367 for (s
=0; s
<n
; s
++) {
368 i
= CFArrayGetCount(nAddrs
);
371 CFEqual(CFArrayGetValueAtIndex(sAddrs
, s
),
372 CFArrayGetValueAtIndex(nAddrs
, i
)) &&
373 CFEqual(CFArrayGetValueAtIndex(sMasks
, s
),
374 CFArrayGetValueAtIndex(nMasks
, i
))
376 /* we have a match */
377 CFArrayRemoveValueAtIndex(nAddrs
, i
);
378 CFArrayRemoveValueAtIndex(nMasks
, i
);
381 CFEqual(CFArrayGetValueAtIndex(sAddrs
, s
),
382 CFArrayGetValueAtIndex(nAddrs
, i
)) &&
383 CFEqual(CFArrayGetValueAtIndex(sDests
, s
),
384 CFArrayGetValueAtIndex(nDests
, i
))
386 /* we have a match */
387 CFArrayRemoveValueAtIndex(nAddrs
, i
);
388 CFArrayRemoveValueAtIndex(nDests
, i
);
396 CFDictionarySetValue(ifDict
,
397 kSCPropNetIPv4Addresses
,
401 CFDictionarySetValue(ifDict
,
402 kSCPropNetIPv4SubnetMasks
,
405 CFDictionarySetValue(ifDict
,
406 kSCPropNetIPv4DestAddresses
,
409 CFDictionarySetValue(interfaces
, ifName
, ifDict
);
412 if (nMasks
) CFRelease(nMasks
);
413 if (nDests
) CFRelease(nDests
);
420 addUnknownService(const void *key
, const void *value
, void *context
)
423 CFMutableDictionaryRef ifDict
= (CFMutableDictionaryRef
)value
;
424 initContextRef info
= (initContextRef
)context
;
428 addrs
= CFDictionaryGetValue(ifDict
, kSCPropNetIPv4Addresses
);
429 if (!addrs
|| (CFArrayGetCount(addrs
) == 0)) {
430 /* if no addresses */
434 /* add the "InterfaceName" to the (new/fake) service dictionary */
435 CFDictionaryAddValue(ifDict
, kSCPropInterfaceName
, (CFStringRef
)key
);
437 /* create a (new/fake) service to hold any remaining addresses */
438 uuid
= CFUUIDCreate(NULL
);
439 service
= CFUUIDCreateString(NULL
, uuid
);
440 CFDictionaryAddValue(info
->aDict
, service
, ifDict
);
441 CFArrayAppendValue(info
->order
, service
);
450 getAddresses(CFDictionaryRef iDict
,
456 *addrs
= isA_CFArray(CFDictionaryGetValue(iDict
,
457 kSCPropNetIPv4Addresses
));
458 *masks
= isA_CFArray(CFDictionaryGetValue(iDict
,
459 kSCPropNetIPv4SubnetMasks
));
460 *dests
= isA_CFArray(CFDictionaryGetValue(iDict
,
461 kSCPropNetIPv4DestAddresses
));
463 if ((*addrs
== NULL
) ||
464 ((*nAddrs
= CFArrayGetCount(*addrs
)) == 0)) {
465 /* sorry, no addresses */
466 _SCErrorSet(kSCStatusReachabilityUnknown
);
470 if (((*masks
== NULL
) && (*dests
== NULL
)) ||
471 ((*masks
!= NULL
) && (*dests
!= NULL
))) {
473 * sorry, we expect to have "SubnetMasks" or
474 * "DestAddresses" (not both) and the count
475 * must match the number of "Addresses".
477 _SCErrorSet(kSCStatusReachabilityUnknown
);
481 if (*masks
&& (*nAddrs
!= CFArrayGetCount(*masks
))) {
482 /* if we don't like the netmasks */
483 _SCErrorSet(kSCStatusReachabilityUnknown
);
487 if (*dests
&& (*nAddrs
!= CFArrayGetCount(*dests
))) {
488 /* if we don't like the destaddresses */
489 _SCErrorSet(kSCStatusReachabilityUnknown
);
497 checkAddress(SCDynamicStoreRef store
,
498 const struct sockaddr
*address
,
500 CFDictionaryRef config
,
501 CFDictionaryRef active
,
502 CFArrayRef serviceOrder
,
503 struct in_addr
*defaultRoute
,
504 SCNetworkConnectionFlags
*flags
)
507 CFStringRef aType
= NULL
;
508 CFDictionaryRef cDict
= NULL
;
510 CFStringRef key
= NULL
;
512 int sc_status
= kSCStatusReachabilityUnknown
;
513 char *statusMessage
= NULL
;
515 if (!address
|| !flags
) {
516 sc_status
= kSCStatusInvalidArgument
;
522 if (address
->sa_family
== AF_INET
) {
523 struct sockaddr_in
*sin
= (struct sockaddr_in
*)address
;
525 SCLog(_sc_debug
, LOG_INFO
, CFSTR("checkAddress(%s)"), inet_ntoa(sin
->sin_addr
));
528 * Check if the address is on one of the subnets
529 * associated with our active IPv4 interfaces
531 aCnt
= CFArrayGetCount(serviceOrder
);
532 for (i
=0; i
<aCnt
; i
++) {
533 CFDictionaryRef aDict
;
540 key
= CFArrayGetValueAtIndex(serviceOrder
, i
);
541 aDict
= CFDictionaryGetValue(active
, key
);
544 !getAddresses(aDict
, &nAddrs
, &addrs
, &masks
, &dests
)) {
545 /* if no addresses to check */
549 for (j
=0; j
<nAddrs
; j
++) {
550 struct in_addr ifAddr
;
552 if (inet_atonCF(CFArrayGetValueAtIndex(addrs
, j
),
554 /* if Addresses string is invalid */
559 struct in_addr ifMask
;
561 if (inet_atonCF(CFArrayGetValueAtIndex(masks
, j
),
563 /* if SubnetMask string is invalid */
567 if ((ntohl(ifAddr
.s_addr
) & ntohl(ifMask
.s_addr
)) ==
568 (ntohl(sin
->sin_addr
.s_addr
) & ntohl(ifMask
.s_addr
))) {
569 /* the requested address is on this subnet */
570 statusMessage
= "isReachable (my subnet)";
571 *flags
|= kSCNetworkFlagsReachable
;
575 struct in_addr destAddr
;
577 /* check remote address */
578 if (inet_atonCF(CFArrayGetValueAtIndex(dests
, j
),
580 /* if DestAddresses string is invalid */
584 /* check local address */
585 if (ntohl(sin
->sin_addr
.s_addr
) == ntohl(ifAddr
.s_addr
)) {
586 /* the address is our side of the link */
587 statusMessage
= "isReachable (my local address)";
588 *flags
|= kSCNetworkFlagsReachable
;
592 if (ntohl(sin
->sin_addr
.s_addr
) == ntohl(destAddr
.s_addr
)) {
593 /* the address is the other side of the link */
594 statusMessage
= "isReachable (my remote address)";
595 *flags
|= kSCNetworkFlagsReachable
;
603 * Check if the address is accessible via the "default" route.
605 for (i
=0; i
<aCnt
; i
++) {
606 CFDictionaryRef aDict
;
613 key
= CFArrayGetValueAtIndex(serviceOrder
, i
);
614 aDict
= CFDictionaryGetValue(active
, key
);
616 if (!sin
->sin_addr
.s_addr
||
619 !getAddresses(aDict
, &nAddrs
, &addrs
, &masks
, &dests
)) {
620 /* if no addresses to check */
624 for (j
=0; defaultRoute
&& j
<nAddrs
; j
++) {
626 struct in_addr ifAddr
;
627 struct in_addr ifMask
;
629 if (inet_atonCF(CFArrayGetValueAtIndex(addrs
, j
),
631 /* if Addresses string is invalid */
635 if (inet_atonCF(CFArrayGetValueAtIndex(masks
, j
),
637 /* if SubnetMasks string is invalid */
641 if ((ntohl(ifAddr
.s_addr
) & ntohl(ifMask
.s_addr
)) ==
642 (ntohl(defaultRoute
->s_addr
) & ntohl(ifMask
.s_addr
))) {
643 /* the requested address is on this subnet */
644 statusMessage
= "isReachable via default route (my subnet)";
645 *flags
|= kSCNetworkFlagsReachable
;
649 struct in_addr destAddr
;
651 /* check remote address */
652 if (inet_atonCF(CFArrayGetValueAtIndex(dests
, j
),
654 /* if DestAddresses string is invalid */
658 if (ntohl(destAddr
.s_addr
) == ntohl(defaultRoute
->s_addr
)) {
659 /* the address is the other side of the link */
660 statusMessage
= "isReachable via default route (my remote address)";
661 *flags
|= kSCNetworkFlagsReachable
;
669 * Check the not active (but configured) IPv4 services
671 for (i
=0; i
<aCnt
; i
++) {
672 key
= CFArrayGetValueAtIndex(serviceOrder
, i
);
674 if (CFDictionaryContainsKey(active
, key
)) {
675 /* if this service is active */
679 cDict
= CFDictionaryGetValue(config
, key
);
681 /* if no configuration for this service */
686 * We have a service which "claims" to be a potential path
687 * off of the system. Check to make sure that this is a
688 * type of PPP link before claiming it's viable.
690 aType
= CFDictionaryGetValue(cDict
, kSCPropNetInterfaceType
);
691 if (!aType
|| !CFEqual(aType
, kSCValNetInterfaceTypePPP
)) {
692 /* if we can't get a connection on this service */
693 sc_status
= kSCStatusOK
;
697 statusMessage
= "is configured w/dynamic addressing";
698 *flags
|= kSCNetworkFlagsTransientConnection
;
699 *flags
|= kSCNetworkFlagsReachable
;
700 *flags
|= kSCNetworkFlagsConnectionRequired
;
703 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = %s"), statusMessage
);
704 SCLog(TRUE
, LOG_INFO
, CFSTR(" service id = %@"), key
);
707 sc_status
= kSCStatusOK
;
711 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" cannot be reached"));
712 sc_status
= kSCStatusOK
;
717 * if no code for this address family (yet)
719 SCLog(_sc_verbose
, LOG_ERR
,
720 CFSTR("checkAddress(): unexpected address family %d"),
722 sc_status
= kSCStatusInvalidArgument
;
731 CFDictionaryRef aDict
;
732 CFStringRef interface
= NULL
;
734 /* attempt to get the interface type from the config info */
735 aDict
= CFDictionaryGetValue(active
, key
);
737 interface
= CFDictionaryGetValue(aDict
, kSCPropInterfaceName
);
740 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = %s"), statusMessage
);
741 SCLog(TRUE
, LOG_INFO
, CFSTR(" service id = %@"), key
);
742 SCLog(TRUE
, LOG_INFO
, CFSTR(" device = %@"), interface
? interface
: CFSTR("?"));
745 sc_status
= kSCStatusOK
;
748 * We have an interface which "claims" to be a valid path
749 * off of the system. Check to make sure that this isn't
750 * a dial-on-demand PPP link that isn't connected yet.
754 CFDictionaryRef cDict
;
756 /* attempt to get the interface type from the config info */
757 cDict
= CFDictionaryGetValue(config
, key
);
759 aType
= CFDictionaryGetValue(cDict
, kSCPropNetInterfaceType
);
762 if (!aType
|| !CFEqual(aType
, kSCValNetInterfaceTypePPP
)) {
764 * if we don't know the interface type or if
765 * it is not a ppp interface
770 num
= CFDictionaryGetValue(cDict
, kSCPropNetPPPDialOnDemand
);
774 CFNumberGetValue(num
, kCFNumberIntType
, &dialOnDemand
);
775 if (dialOnDemand
!= 0) {
776 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
782 *flags
|= kSCNetworkFlagsTransientConnection
;
786 struct ppp_status
*pppLinkStatus
;
790 * The service ID is available, ask the PPP controller
791 * for the extended status.
793 pppStatus
= PPPInit(&pppRef
);
794 if (pppStatus
!= 0) {
795 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR(" PPPInit() failed: status=%d"), pppStatus
);
796 sc_status
= kSCStatusReachabilityUnknown
;
800 pppStatus
= PPPGetLinkByServiceID(pppRef
, key
, &pppLink
);
801 if (pppStatus
!= 0) {
802 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR(" PPPGetLinkByServiceID() failed: status=%d"), pppStatus
);
803 sc_status
= kSCStatusReachabilityUnknown
;
807 pppStatus
= PPPStatus(pppRef
, pppLink
, &pppLinkStatus
);
808 if (pppStatus
!= 0) {
809 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR(" PPPStatus() failed: status=%d"), pppStatus
);
810 sc_status
= kSCStatusReachabilityUnknown
;
814 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR(" PPP link status = %d"), pppLinkStatus
->status
);
816 switch (pppLinkStatus
->status
) {
818 /* if we're really UP and RUNNING */
821 /* if we're effectively UP and RUNNING */
824 /* if we're not connected at all */
825 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
826 *flags
|= kSCNetworkFlagsReachable
;
827 *flags
|= kSCNetworkFlagsConnectionRequired
;
828 sc_status
= kSCStatusOK
;
831 /* if we're in the process of [dis]connecting */
832 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link, connection in progress"));
833 *flags
|= kSCNetworkFlagsReachable
;
834 *flags
|= kSCNetworkFlagsConnectionRequired
;
835 sc_status
= kSCStatusOK
;
838 CFAllocatorDeallocate(NULL
, pppLinkStatus
);
845 if (pppRef
!= -1) (void) PPPDispose(pppRef
);
847 if (sc_status
!= kSCStatusOK
) {
848 _SCErrorSet(sc_status
);
857 _CheckReachabilityInit(SCDynamicStoreRef store
,
858 CFDictionaryRef
*config
,
859 CFDictionaryRef
*active
,
860 CFArrayRef
*serviceOrder
,
861 struct in_addr
**defaultRoute
)
863 CFMutableDictionaryRef activeDict
;
864 CFMutableDictionaryRef configDict
;
866 CFDictionaryRef dict
;
867 CFMutableDictionaryRef interfaces
;
868 CFMutableArrayRef keys
;
869 CFMutableArrayRef orderArray
;
870 CFDictionaryRef orderDict
;
871 CFStringRef orderKey
;
873 CFMutableArrayRef patterns
;
874 CFStringRef routeKey
;
875 CFDictionaryRef routeDict
;
877 configDict
= CFDictionaryCreateMutable(NULL
,
879 &kCFTypeDictionaryKeyCallBacks
,
880 &kCFTypeDictionaryValueCallBacks
);
881 *config
= configDict
;
883 activeDict
= CFDictionaryCreateMutable(NULL
,
885 &kCFTypeDictionaryKeyCallBacks
,
886 &kCFTypeDictionaryValueCallBacks
);
887 *active
= activeDict
;
889 orderArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
890 *serviceOrder
= orderArray
;
892 *defaultRoute
= NULL
;
894 interfaces
= CFDictionaryCreateMutable(NULL
,
896 &kCFTypeDictionaryKeyCallBacks
,
897 &kCFTypeDictionaryValueCallBacks
);
900 * collect information on the configured services and their
901 * associated interface type.
903 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
904 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
907 * Setup:/Network/Global/IPv4 (for the ServiceOrder)
909 orderKey
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
910 kSCDynamicStoreDomainSetup
,
912 CFArrayAppendValue(keys
, orderKey
);
915 * State:/Network/Global/IPv4 (for the DefaultRoute)
917 routeKey
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
918 kSCDynamicStoreDomainState
,
920 CFArrayAppendValue(keys
, routeKey
);
922 /* Setup: per-service IPv4 info */
923 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
924 kSCDynamicStoreDomainSetup
,
927 CFArrayAppendValue(patterns
, pattern
);
930 /* Setup: per-service Interface info */
931 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
932 kSCDynamicStoreDomainSetup
,
935 CFArrayAppendValue(patterns
, pattern
);
938 /* Setup: per-service PPP info */
939 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
940 kSCDynamicStoreDomainSetup
,
943 CFArrayAppendValue(patterns
, pattern
);
946 /* State: per-service IPv4 info */
947 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
948 kSCDynamicStoreDomainState
,
951 CFArrayAppendValue(patterns
, pattern
);
954 /* State: per-interface IPv4 info */
955 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
956 kSCDynamicStoreDomainState
,
959 CFArrayAppendValue(patterns
, pattern
);
962 /* fetch the configuration information */
963 dict
= SCDynamicStoreCopyMultiple(store
, keys
, patterns
);
971 * get the ServiceOrder key from the global settings.
973 orderDict
= CFDictionaryGetValue(dict
, orderKey
);
974 if (isA_CFDictionary(orderDict
)) {
977 /* global settings are available */
978 array
= (CFMutableArrayRef
)CFDictionaryGetValue(orderDict
, kSCPropNetServiceOrder
);
979 if (isA_CFArray(array
)) {
980 CFArrayAppendArray(orderArray
,
982 CFRangeMake(0, CFArrayGetCount(array
)));
987 * get the DefaultRoute
989 routeDict
= CFDictionaryGetValue(dict
, routeKey
);
990 if (isA_CFDictionary(routeDict
)) {
993 /* global state is available, get default route */
994 addr
= CFDictionaryGetValue(routeDict
, kSCPropNetIPv4Router
);
995 if (isA_CFString(addr
)) {
996 struct in_addr
*route
;
998 route
= CFAllocatorAllocate(NULL
, sizeof(struct in_addr
), 0);
999 if (inet_atonCF(addr
, route
) == 0) {
1000 /* if address string is invalid */
1001 CFAllocatorDeallocate(NULL
, route
);
1004 *defaultRoute
= route
;
1010 * collect the configured services, the active services, and
1011 * the active interfaces.
1013 context
.cDict
= configDict
;
1014 context
.cPrefix
= SCDynamicStoreKeyCreate(NULL
,
1016 kSCDynamicStoreDomainSetup
,
1019 context
.aDict
= activeDict
;
1020 context
.aPrefix
= SCDynamicStoreKeyCreate(NULL
,
1022 kSCDynamicStoreDomainState
,
1025 context
.iDict
= interfaces
;
1026 context
.iPrefix
= SCDynamicStoreKeyCreate(NULL
,
1028 kSCDynamicStoreDomainState
,
1031 context
.order
= orderArray
;
1033 CFDictionaryApplyFunction(dict
, collectInfo
, &context
);
1036 * add additional information for the configured services
1038 CFDictionaryApplyFunction(dict
, collectExtraInfo
, &context
);
1041 * remove any addresses associated with known services
1043 CFDictionaryApplyFunction(activeDict
, removeKnownAddresses
, interfaces
);
1046 * create new services for any remaining addresses
1048 CFDictionaryApplyFunction(interfaces
, addUnknownService
, &context
);
1050 CFRelease(context
.cPrefix
);
1051 CFRelease(context
.aPrefix
);
1052 CFRelease(context
.iPrefix
);
1057 CFRelease(interfaces
);
1058 CFRelease(orderKey
);
1059 CFRelease(routeKey
);
1062 SCLog(_sc_debug
, LOG_NOTICE
, CFSTR("config = %@"), *config
);
1063 SCLog(_sc_debug
, LOG_NOTICE
, CFSTR("active = %@"), *active
);
1064 SCLog(_sc_debug
, LOG_NOTICE
, CFSTR("serviceOrder = %@"), *serviceOrder
);
1065 SCLog(_sc_debug
, LOG_NOTICE
, CFSTR("defaultRoute = %s"), *defaultRoute
?inet_ntoa(**defaultRoute
):"None");
1072 _CheckReachabilityFree(CFDictionaryRef config
,
1073 CFDictionaryRef active
,
1074 CFArrayRef serviceOrder
,
1075 struct in_addr
*defaultRoute
)
1077 if (config
) CFRelease(config
);
1078 if (active
) CFRelease(active
);
1079 if (serviceOrder
) CFRelease(serviceOrder
);
1080 if (defaultRoute
) CFAllocatorDeallocate(NULL
, defaultRoute
);
1086 SCNetworkCheckReachabilityByAddress(const struct sockaddr
*address
,
1088 SCNetworkConnectionFlags
*flags
)
1090 CFDictionaryRef active
= NULL
;
1091 CFDictionaryRef config
= NULL
;
1092 struct in_addr
*defaultRoute
= NULL
;
1094 CFArrayRef serviceOrder
= NULL
;
1095 SCDynamicStoreRef store
= NULL
;
1102 if (address
->sa_family
== AF_INET
) {
1103 struct sockaddr_in
*sin
= (struct sockaddr_in
*)address
;
1105 if (sin
->sin_addr
.s_addr
== 0) {
1106 SCLog(_sc_debug
, LOG_INFO
, CFSTR("checkAddress(0.0.0.0)"));
1107 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = isReachable (this host)"));
1108 *flags
|= kSCNetworkFlagsReachable
;
1113 store
= SCDynamicStoreCreate(NULL
,
1114 CFSTR("SCNetworkCheckReachabilityByAddress"),
1118 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
1122 _CheckReachabilityInit(store
, &config
, &active
, &serviceOrder
, &defaultRoute
);
1123 ok
= checkAddress(store
,
1131 _CheckReachabilityFree(config
, active
, serviceOrder
, defaultRoute
);
1139 * rankReachability()
1140 * Not reachable == 0
1141 * Connection Required == 1
1145 rankReachability(int flags
)
1149 if (flags
& kSCNetworkFlagsReachable
) rank
= 2;
1150 if (flags
& kSCNetworkFlagsConnectionRequired
) rank
= 1;
1156 SCNetworkCheckReachabilityByName(const char *nodename
,
1157 SCNetworkConnectionFlags
*flags
)
1159 CFDictionaryRef active
= NULL
;
1160 CFDictionaryRef config
= NULL
;
1161 struct in_addr
*defaultRoute
= NULL
;
1163 Boolean haveDNS
= FALSE
;
1166 #ifdef CHECK_IPV6_REACHABILITY
1167 struct addrinfo
*res
= NULL
;
1168 struct addrinfo
*resP
;
1169 #endif /* CHECK_IPV6_REACHABILITY */
1170 CFArrayRef serviceOrder
= NULL
;
1171 SCDynamicStoreRef store
= NULL
;
1173 store
= SCDynamicStoreCreate(NULL
,
1174 CFSTR("SCNetworkCheckReachabilityByName"),
1178 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
1182 _CheckReachabilityInit(store
, &config
, &active
, &serviceOrder
, &defaultRoute
);
1185 * We first assume that all of the configured DNS servers
1186 * are available. Since we don't know which name server will
1187 * be consulted to resolve the specified nodename we need to
1188 * check the availability of ALL name servers. We can only
1189 * proceed if we know that our query can be answered.
1192 *flags
= kSCNetworkFlagsReachable
;
1195 for (i
=0; i
<_res
.nscount
; i
++) {
1196 SCNetworkConnectionFlags ns_flags
= 0;
1198 if (_res
.nsaddr_list
[i
].sin_addr
.s_addr
== 0) {
1204 if (_res
.nsaddr_list
[i
].sin_len
== 0) {
1205 _res
.nsaddr_list
[i
].sin_len
= sizeof(_res
.nsaddr_list
[i
]);
1208 ok
= checkAddress(store
,
1209 (struct sockaddr
*)&_res
.nsaddr_list
[i
],
1210 _res
.nsaddr_list
[i
].sin_len
,
1220 if (rankReachability(ns_flags
) < rankReachability(*flags
)) {
1221 /* return the worst case result */
1226 if (!ok
|| (rankReachability(*flags
) < 2)) {
1230 SCLog(_sc_debug
, LOG_INFO
, CFSTR("check DNS for \"%s\""), nodename
);
1233 * OK, all of the DNS name servers are available. Let's
1234 * first assume that the requested host is NOT available,
1235 * resolve the nodename, and check its address for
1236 * accessibility. We return the best status available.
1241 * resolve the nodename into an address
1244 #ifdef CHECK_IPV6_REACHABILITY
1245 i
= getaddrinfo(nodename
, NULL
, NULL
, &res
);
1247 SCLog(_sc_verbose
, LOG_ERR
,
1248 CFSTR("getaddrinfo() failed: %s"),
1253 for (resP
=res
; resP
!=NULL
; resP
=resP
->ai_next
) {
1254 SCNetworkConnectionFlags ns_flags
= 0;
1256 if (resP
->ai_addr
->sa_family
== AF_INET
) {
1257 struct sockaddr_in
*sin
= (struct sockaddr_in
*)resP
->ai_addr
;
1259 if (sin
->sin_addr
.s_addr
== 0) {
1260 SCLog(_sc_debug
, LOG_INFO
, CFSTR("checkAddress(0.0.0.0)"));
1261 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = isReachable (this host)"));
1262 *flags
|= kSCNetworkFlagsReachable
;
1267 ok
= checkAddress(store
,
1279 if (rankReachability(ns_flags
) > rankReachability(*flags
)) {
1280 /* return the best case result */
1282 if (rankReachability(*flags
) == 2) {
1294 * The getaddrinfo() function call didn't return any addresses. While
1295 * this may be the correct answer we have found that some DNS servers
1296 * may, depending on what has been cached, not return all available
1297 * records when issued a T_ANY query. To accomodate these servers
1298 * we double check by using the gethostbyname() function which uses
1299 * a simple T_A query.
1305 CFSTR("getaddrinfo() returned no addresses, try gethostbyname()"));
1307 #endif /* CHECK_IPV6_REACHABILITY */
1309 h
= gethostbyname(nodename
);
1310 if (h
&& h
->h_length
) {
1311 struct in_addr
**s
= (struct in_addr
**)h
->h_addr_list
;
1314 SCNetworkConnectionFlags ns_flags
= 0;
1315 struct sockaddr_in sa
;
1317 bzero(&sa
, sizeof(sa
));
1318 sa
.sin_len
= sizeof(sa
);
1319 sa
.sin_family
= AF_INET
;
1322 if (sa
.sin_addr
.s_addr
== 0) {
1323 SCLog(_sc_debug
, LOG_INFO
, CFSTR("checkAddress(0.0.0.0)"));
1324 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = isReachable (this host)"));
1325 *flags
|= kSCNetworkFlagsReachable
;
1329 ok
= checkAddress(store
,
1330 (struct sockaddr
*)&sa
,
1341 if (rankReachability(ns_flags
) > rankReachability(*flags
)) {
1342 /* return the best case result */
1344 if (rankReachability(*flags
) == 2) {
1356 case NETDB_INTERNAL
:
1357 msg
= strerror(errno
);
1359 case HOST_NOT_FOUND
:
1360 msg
= "Host not found.";
1363 * No DNS servers are defined. Set flags based on
1364 * the availability of configured (but not active)
1367 struct sockaddr_in sa
;
1369 bzero(&sa
, sizeof(sa
));
1370 sa
.sin_len
= sizeof(sa
);
1371 sa
.sin_family
= AF_INET
;
1372 sa
.sin_addr
.s_addr
= 0;
1373 ok
= checkAddress(store
,
1374 (struct sockaddr
*)&sa
,
1382 (*flags
& kSCNetworkFlagsReachable
) &&
1383 (*flags
& kSCNetworkFlagsConnectionRequired
)) {
1385 * We might pick up a set of DNS servers
1386 * from this connection, don't reply with
1387 * "Host not found." just yet.
1398 msg
= "No recovery.";
1401 msg
= "No data available.";
1407 SCLog(_sc_debug
, LOG_INFO
, CFSTR("gethostbyname() failed: %s"), msg
);
1412 _CheckReachabilityFree(config
, active
, serviceOrder
, defaultRoute
);
1413 if (store
) CFRelease(store
);
1414 #ifdef CHECK_IPV6_REACHABILITY
1415 if (res
) freeaddrinfo(res
);
1416 #endif /* CHECK_IPV6_REACHABILITY */