2 * Copyright (c) 2000 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 not connected at all */
822 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
823 *flags
|= kSCNetworkFlagsReachable
;
824 *flags
|= kSCNetworkFlagsConnectionRequired
;
825 sc_status
= kSCStatusOK
;
828 /* if we're in the process of [dis]connecting */
829 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link, connection in progress"));
830 *flags
|= kSCNetworkFlagsReachable
;
831 *flags
|= kSCNetworkFlagsConnectionRequired
;
832 sc_status
= kSCStatusOK
;
835 CFAllocatorDeallocate(NULL
, pppLinkStatus
);
842 if (pppRef
!= -1) (void) PPPDispose(pppRef
);
844 if (sc_status
!= kSCStatusOK
) {
845 _SCErrorSet(sc_status
);
854 _CheckReachabilityInit(SCDynamicStoreRef store
,
855 CFDictionaryRef
*config
,
856 CFDictionaryRef
*active
,
857 CFArrayRef
*serviceOrder
,
858 struct in_addr
**defaultRoute
)
860 CFMutableDictionaryRef activeDict
;
861 CFMutableDictionaryRef configDict
;
863 CFDictionaryRef dict
;
864 CFMutableDictionaryRef interfaces
;
865 CFMutableArrayRef keys
;
866 CFMutableArrayRef orderArray
;
867 CFDictionaryRef orderDict
;
868 CFStringRef orderKey
;
870 CFMutableArrayRef patterns
;
871 CFStringRef routeKey
;
872 CFDictionaryRef routeDict
;
874 configDict
= CFDictionaryCreateMutable(NULL
,
876 &kCFTypeDictionaryKeyCallBacks
,
877 &kCFTypeDictionaryValueCallBacks
);
878 *config
= configDict
;
880 activeDict
= CFDictionaryCreateMutable(NULL
,
882 &kCFTypeDictionaryKeyCallBacks
,
883 &kCFTypeDictionaryValueCallBacks
);
884 *active
= activeDict
;
886 orderArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
887 *serviceOrder
= orderArray
;
889 *defaultRoute
= NULL
;
891 interfaces
= CFDictionaryCreateMutable(NULL
,
893 &kCFTypeDictionaryKeyCallBacks
,
894 &kCFTypeDictionaryValueCallBacks
);
897 * collect information on the configured services and their
898 * associated interface type.
900 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
901 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
904 * Setup:/Network/Global/IPv4 (for the ServiceOrder)
906 orderKey
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
907 kSCDynamicStoreDomainSetup
,
909 CFArrayAppendValue(keys
, orderKey
);
912 * State:/Network/Global/IPv4 (for the DefaultRoute)
914 routeKey
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
915 kSCDynamicStoreDomainState
,
917 CFArrayAppendValue(keys
, routeKey
);
919 /* Setup: per-service IPv4 info */
920 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
921 kSCDynamicStoreDomainSetup
,
924 CFArrayAppendValue(patterns
, pattern
);
927 /* Setup: per-service Interface info */
928 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
929 kSCDynamicStoreDomainSetup
,
932 CFArrayAppendValue(patterns
, pattern
);
935 /* Setup: per-service PPP info */
936 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
937 kSCDynamicStoreDomainSetup
,
940 CFArrayAppendValue(patterns
, pattern
);
943 /* State: per-service IPv4 info */
944 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
945 kSCDynamicStoreDomainState
,
948 CFArrayAppendValue(patterns
, pattern
);
951 /* State: per-interface IPv4 info */
952 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
953 kSCDynamicStoreDomainState
,
956 CFArrayAppendValue(patterns
, pattern
);
959 /* fetch the configuration information */
960 dict
= SCDynamicStoreCopyMultiple(store
, keys
, patterns
);
968 * get the ServiceOrder key from the global settings.
970 orderDict
= CFDictionaryGetValue(dict
, orderKey
);
971 if (isA_CFDictionary(orderDict
)) {
974 /* global settings are available */
975 array
= (CFMutableArrayRef
)CFDictionaryGetValue(orderDict
, kSCPropNetServiceOrder
);
976 if (isA_CFArray(array
)) {
977 CFArrayAppendArray(orderArray
,
979 CFRangeMake(0, CFArrayGetCount(array
)));
984 * get the DefaultRoute
986 routeDict
= CFDictionaryGetValue(dict
, routeKey
);
987 if (isA_CFDictionary(routeDict
)) {
990 /* global state is available, get default route */
991 addr
= CFDictionaryGetValue(routeDict
, kSCPropNetIPv4Router
);
992 if (isA_CFString(addr
)) {
993 struct in_addr
*route
;
995 route
= CFAllocatorAllocate(NULL
, sizeof(struct in_addr
), 0);
996 if (inet_atonCF(addr
, route
) == 0) {
997 /* if address string is invalid */
998 CFAllocatorDeallocate(NULL
, route
);
1001 *defaultRoute
= route
;
1007 * collect the configured services, the active services, and
1008 * the active interfaces.
1010 context
.cDict
= configDict
;
1011 context
.cPrefix
= SCDynamicStoreKeyCreate(NULL
,
1013 kSCDynamicStoreDomainSetup
,
1016 context
.aDict
= activeDict
;
1017 context
.aPrefix
= SCDynamicStoreKeyCreate(NULL
,
1019 kSCDynamicStoreDomainState
,
1022 context
.iDict
= interfaces
;
1023 context
.iPrefix
= SCDynamicStoreKeyCreate(NULL
,
1025 kSCDynamicStoreDomainState
,
1028 context
.order
= orderArray
;
1030 CFDictionaryApplyFunction(dict
, collectInfo
, &context
);
1033 * add additional information for the configured services
1035 CFDictionaryApplyFunction(dict
, collectExtraInfo
, &context
);
1038 * remove any addresses associated with known services
1040 CFDictionaryApplyFunction(activeDict
, removeKnownAddresses
, interfaces
);
1043 * create new services for any remaining addresses
1045 CFDictionaryApplyFunction(interfaces
, addUnknownService
, &context
);
1047 CFRelease(context
.cPrefix
);
1048 CFRelease(context
.aPrefix
);
1049 CFRelease(context
.iPrefix
);
1054 CFRelease(interfaces
);
1055 CFRelease(orderKey
);
1056 CFRelease(routeKey
);
1059 SCLog(_sc_debug
, LOG_NOTICE
, CFSTR("config = %@"), *config
);
1060 SCLog(_sc_debug
, LOG_NOTICE
, CFSTR("active = %@"), *active
);
1061 SCLog(_sc_debug
, LOG_NOTICE
, CFSTR("serviceOrder = %@"), *serviceOrder
);
1062 SCLog(_sc_debug
, LOG_NOTICE
, CFSTR("defaultRoute = %s"), *defaultRoute
?inet_ntoa(**defaultRoute
):"None");
1069 _CheckReachabilityFree(CFDictionaryRef config
,
1070 CFDictionaryRef active
,
1071 CFArrayRef serviceOrder
,
1072 struct in_addr
*defaultRoute
)
1074 if (config
) CFRelease(config
);
1075 if (active
) CFRelease(active
);
1076 if (serviceOrder
) CFRelease(serviceOrder
);
1077 if (defaultRoute
) CFAllocatorDeallocate(NULL
, defaultRoute
);
1083 SCNetworkCheckReachabilityByAddress(const struct sockaddr
*address
,
1085 SCNetworkConnectionFlags
*flags
)
1087 CFDictionaryRef active
= NULL
;
1088 CFDictionaryRef config
= NULL
;
1089 struct in_addr
*defaultRoute
= NULL
;
1091 CFArrayRef serviceOrder
= NULL
;
1092 SCDynamicStoreRef store
= NULL
;
1099 if (address
->sa_family
== AF_INET
) {
1100 struct sockaddr_in
*sin
= (struct sockaddr_in
*)address
;
1102 if (sin
->sin_addr
.s_addr
== 0) {
1103 SCLog(_sc_debug
, LOG_INFO
, CFSTR("checkAddress(0.0.0.0)"));
1104 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = isReachable (this host)"));
1105 *flags
|= kSCNetworkFlagsReachable
;
1110 store
= SCDynamicStoreCreate(NULL
,
1111 CFSTR("SCNetworkCheckReachabilityByAddress"),
1115 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
1119 _CheckReachabilityInit(store
, &config
, &active
, &serviceOrder
, &defaultRoute
);
1120 ok
= checkAddress(store
,
1128 _CheckReachabilityFree(config
, active
, serviceOrder
, defaultRoute
);
1136 * rankReachability()
1137 * Not reachable == 0
1138 * Connection Required == 1
1142 rankReachability(int flags
)
1146 if (flags
& kSCNetworkFlagsReachable
) rank
= 2;
1147 if (flags
& kSCNetworkFlagsConnectionRequired
) rank
= 1;
1153 SCNetworkCheckReachabilityByName(const char *nodename
,
1154 SCNetworkConnectionFlags
*flags
)
1156 CFDictionaryRef active
= NULL
;
1157 CFDictionaryRef config
= NULL
;
1158 struct in_addr
*defaultRoute
= NULL
;
1160 Boolean haveDNS
= FALSE
;
1163 struct addrinfo
*res
= NULL
;
1164 struct addrinfo
*resP
;
1165 CFArrayRef serviceOrder
= NULL
;
1166 SCDynamicStoreRef store
= NULL
;
1168 store
= SCDynamicStoreCreate(NULL
,
1169 CFSTR("SCNetworkCheckReachabilityByName"),
1173 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
1177 _CheckReachabilityInit(store
, &config
, &active
, &serviceOrder
, &defaultRoute
);
1180 * We first assume that all of the configured DNS servers
1181 * are available. Since we don't know which name server will
1182 * be consulted to resolve the specified nodename we need to
1183 * check the availability of ALL name servers. We can only
1184 * proceed if we know that our query can be answered.
1187 *flags
= kSCNetworkFlagsReachable
;
1190 for (i
=0; i
<_res
.nscount
; i
++) {
1191 SCNetworkConnectionFlags ns_flags
= 0;
1193 if (_res
.nsaddr_list
[i
].sin_addr
.s_addr
== 0) {
1199 if (_res
.nsaddr_list
[i
].sin_len
== 0) {
1200 _res
.nsaddr_list
[i
].sin_len
= sizeof(_res
.nsaddr_list
[i
]);
1203 ok
= checkAddress(store
,
1204 (struct sockaddr
*)&_res
.nsaddr_list
[i
],
1205 _res
.nsaddr_list
[i
].sin_len
,
1215 if (rankReachability(ns_flags
) < rankReachability(*flags
)) {
1216 /* return the worst case result */
1221 if (!ok
|| (rankReachability(*flags
) < 2)) {
1225 SCLog(_sc_debug
, LOG_INFO
, CFSTR("check DNS for \"%s\""), nodename
);
1228 * OK, all of the DNS name servers are available. Let's
1229 * first assume that the requested host is NOT available,
1230 * resolve the nodename, and check its address for
1231 * accessibility. We return the best status available.
1236 * resolve the nodename into an address
1238 i
= getaddrinfo(nodename
, NULL
, NULL
, &res
);
1240 SCLog(_sc_verbose
, LOG_ERR
,
1241 CFSTR("getaddrinfo() failed: %s"),
1246 for (resP
=res
; resP
!=NULL
; resP
=resP
->ai_next
) {
1247 SCNetworkConnectionFlags ns_flags
= 0;
1249 if (resP
->ai_addr
->sa_family
== AF_INET
) {
1250 struct sockaddr_in
*sin
= (struct sockaddr_in
*)resP
->ai_addr
;
1252 if (sin
->sin_addr
.s_addr
== 0) {
1253 SCLog(_sc_debug
, LOG_INFO
, CFSTR("checkAddress(0.0.0.0)"));
1254 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = isReachable (this host)"));
1255 *flags
|= kSCNetworkFlagsReachable
;
1260 ok
= checkAddress(store
,
1272 if (rankReachability(ns_flags
) > rankReachability(*flags
)) {
1273 /* return the best case result */
1275 if (rankReachability(*flags
) == 2) {
1287 * The getaddrinfo() function call didn't return any addresses. While
1288 * this may be the correct answer we have found that some DNS servers
1289 * may, depending on what has been cached, not return all available
1290 * records when issued a T_ANY query. To accomodate these servers
1291 * we double check by using the gethostbyname() function which uses
1292 * a simple T_A query.
1298 CFSTR("getaddrinfo() returned no addresses, try gethostbyname()"));
1301 h
= gethostbyname(nodename
);
1302 if (h
&& h
->h_length
) {
1303 struct in_addr
**s
= (struct in_addr
**)h
->h_addr_list
;
1306 SCNetworkConnectionFlags ns_flags
= 0;
1307 struct sockaddr_in sa
;
1309 bzero(&sa
, sizeof(sa
));
1310 sa
.sin_len
= sizeof(sa
);
1311 sa
.sin_family
= AF_INET
;
1314 if (sa
.sin_addr
.s_addr
== 0) {
1315 SCLog(_sc_debug
, LOG_INFO
, CFSTR("checkAddress(0.0.0.0)"));
1316 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = isReachable (this host)"));
1317 *flags
|= kSCNetworkFlagsReachable
;
1321 ok
= checkAddress(store
,
1322 (struct sockaddr
*)&sa
,
1333 if (rankReachability(ns_flags
) > rankReachability(*flags
)) {
1334 /* return the best case result */
1336 if (rankReachability(*flags
) == 2) {
1348 case NETDB_INTERNAL
:
1349 msg
= strerror(errno
);
1351 case HOST_NOT_FOUND
:
1352 msg
= "Host not found.";
1355 * No DNS servers are defined. Set flags based on
1356 * the availability of configured (but not active)
1359 struct sockaddr_in sa
;
1361 bzero(&sa
, sizeof(sa
));
1362 sa
.sin_len
= sizeof(sa
);
1363 sa
.sin_family
= AF_INET
;
1364 sa
.sin_addr
.s_addr
= 0;
1365 ok
= checkAddress(store
,
1366 (struct sockaddr
*)&sa
,
1374 (*flags
& kSCNetworkFlagsReachable
) &&
1375 (*flags
& kSCNetworkFlagsConnectionRequired
)) {
1377 * We might pick up a set of DNS servers
1378 * from this connection, don't reply with
1379 * "Host not found." just yet.
1390 msg
= "No recovery.";
1393 msg
= "No data available.";
1399 SCLog(_sc_debug
, LOG_INFO
, CFSTR("gethostbyname() failed: %s"), msg
);
1404 _CheckReachabilityFree(config
, active
, serviceOrder
, defaultRoute
);
1405 if (store
) CFRelease(store
);
1406 if (res
) freeaddrinfo(res
);