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@
23 #include <SystemConfiguration/SystemConfiguration.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <arpa/nameser.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
39 inet_atonCF(CFStringRef cfStr
, struct in_addr
*addr
)
41 char cStr
[sizeof("255.255.255.255")];
43 if (!CFStringGetCString(cfStr
, cStr
, sizeof(cStr
), kCFStringEncodingMacRoman
)) {
47 return inet_aton(cStr
, addr
);
52 * Function: parse_component
54 * Given a string 'key' and a string prefix 'prefix',
55 * return the next component in the slash '/' separated
59 * 1. key = "a/b/c" prefix = "a/"
61 * 2. key = "a/b/c" prefix = "a/b/"
65 parse_component(CFStringRef key
, CFStringRef prefix
)
67 CFMutableStringRef comp
;
70 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
73 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
74 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
75 range
= CFStringFind(comp
, CFSTR("/"), 0);
76 if (range
.location
== kCFNotFound
) {
79 range
.length
= CFStringGetLength(comp
) - range
.location
;
80 CFStringDelete(comp
, range
);
86 * return a dictionary of configured services.
88 static CFDictionaryRef
89 getServices(SCDSessionRef session
)
91 CFArrayRef defined
= NULL
;
95 CFMutableDictionaryRef services
;
98 prefix
= SCDKeyCreate(CFSTR("%@/%@/%@/"),
103 services
= CFDictionaryCreateMutable(NULL
,
105 &kCFTypeDictionaryKeyCallBacks
,
106 &kCFTypeDictionaryValueCallBacks
);
108 key
= SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup
,
111 status
= SCDList(session
, key
, kSCDRegexKey
, &defined
);
113 if (status
!= SCD_OK
) {
117 for (i
= 0; i
< CFArrayGetCount(defined
); i
++) {
118 CFDictionaryRef if_dict
;
119 SCDHandleRef if_handle
= NULL
;
120 CFDictionaryRef ip_dict
;
121 SCDHandleRef ip_handle
= NULL
;
122 boolean_t isPPP
= FALSE
;
123 CFDictionaryRef ppp_dict
;
124 SCDHandleRef ppp_handle
= NULL
;
125 CFMutableDictionaryRef sDict
= NULL
;
126 CFStringRef sid
= NULL
;
128 key
= CFArrayGetValueAtIndex(defined
, i
);
130 /* get IPv4 dictionary for service */
131 status
= SCDGet(session
, key
, &ip_handle
);
132 if (status
!= SCD_OK
) {
133 /* if service was removed behind our back */
136 ip_dict
= SCDHandleGetData(ip_handle
);
138 sDict
= CFDictionaryCreateMutableCopy(NULL
, 0, ip_dict
);
140 /* add keys from the service's Interface dictionary */
141 sid
= parse_component(key
, prefix
);
146 key
= SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup
,
149 status
= SCDGet(session
, key
, &if_handle
);
151 if (status
!= SCD_OK
) {
154 if_dict
= SCDHandleGetData(if_handle
);
156 /* check the interface "Type", "SubType", and "DeviceName" */
157 if (CFDictionaryGetValueIfPresent(if_dict
,
158 kSCPropNetInterfaceType
,
160 CFDictionaryAddValue(sDict
, kSCPropNetInterfaceType
, key
);
161 isPPP
= CFEqual(key
, kSCValNetInterfaceTypePPP
);
163 if (CFDictionaryGetValueIfPresent(if_dict
,
164 kSCPropNetInterfaceSubType
,
166 CFDictionaryAddValue(sDict
, kSCPropNetInterfaceSubType
, key
);
170 key
= SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup
,
173 status
= SCDGet(session
, key
, &ppp_handle
);
175 if (status
!= SCD_OK
) {
178 ppp_dict
= SCDHandleGetData(ppp_handle
);
180 /* get Dial-on-Traffic flag */
181 if (CFDictionaryGetValueIfPresent(ppp_dict
,
182 kSCPropNetPPPDialOnDemand
,
184 CFDictionaryAddValue(sDict
, kSCPropNetPPPDialOnDemand
, key
);
188 CFDictionaryAddValue(services
, sid
, sDict
);
192 if (sid
) CFRelease(sid
);
193 if (if_handle
) SCDHandleRelease(if_handle
);
194 if (ip_handle
) SCDHandleRelease(ip_handle
);
195 if (ppp_handle
) SCDHandleRelease(ppp_handle
);
196 if (sDict
) CFRelease(sDict
);
201 if (defined
) CFRelease(defined
);
209 * return a dictionary of configured interfaces.
211 static CFDictionaryRef
212 getInterfaces(SCDSessionRef session
)
214 CFMutableArrayRef defined
= NULL
;
217 CFMutableDictionaryRef interfaces
;
221 prefix
= SCDKeyCreate(CFSTR("%@/%@/%@/"),
226 interfaces
= CFDictionaryCreateMutable(NULL
,
228 &kCFTypeDictionaryKeyCallBacks
,
229 &kCFTypeDictionaryValueCallBacks
);
231 key
= SCDKeyCreateNetworkInterfaceEntity(kSCCacheDomainState
,
234 status
= SCDList(session
, key
, kSCDRegexKey
, &defined
);
236 if (status
!= SCD_OK
) {
240 for (i
=0; i
<CFArrayGetCount(defined
); i
++) {
241 CFStringRef iid
= NULL
;
242 CFDictionaryRef ip_dict
;
243 SCDHandleRef ip_handle
= NULL
;
245 key
= CFArrayGetValueAtIndex(defined
, i
);
247 /* get IPv4 dictionary for service */
248 status
= SCDGet(session
, key
, &ip_handle
);
249 if (status
!= SCD_OK
) {
250 /* if interface was removed behind our back */
253 ip_dict
= SCDHandleGetData(ip_handle
);
255 iid
= parse_component(key
, prefix
);
260 CFDictionaryAddValue(interfaces
, iid
, ip_dict
);
264 if (iid
) CFRelease(iid
);
265 if (ip_handle
) SCDHandleRelease(ip_handle
);
270 if (defined
) CFRelease(defined
);
277 * return an array of interface names based on a specified service order.
280 getInterfaceOrder(CFDictionaryRef interfaces
,
281 CFArrayRef serviceOrder
,
282 CFNumberRef pppOverridePrimary
)
286 CFMutableArrayRef iKeys
;
288 CFMutableArrayRef order
= NULL
;
291 order
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
293 iCnt
= CFDictionaryGetCount(interfaces
);
294 keys
= CFAllocatorAllocate(NULL
, iCnt
* sizeof(CFStringRef
), 0);
295 CFDictionaryGetKeysAndValues(interfaces
, keys
, NULL
);
296 tKeys
= CFArrayCreate(NULL
, keys
, iCnt
, &kCFTypeArrayCallBacks
);
297 CFAllocatorDeallocate(NULL
, keys
);
298 iKeys
= CFArrayCreateMutableCopy(NULL
, 0, tKeys
);
301 for (i
= 0; serviceOrder
&& i
< CFArrayGetCount(serviceOrder
); i
++) {
305 oSID
= CFArrayGetValueAtIndex(serviceOrder
, i
);
306 for (j
=0; j
<CFArrayGetCount(iKeys
); j
++) {
307 CFDictionaryRef iDict
;
312 boolean_t match
= FALSE
;
314 iKey
= CFArrayGetValueAtIndex(iKeys
, j
);
315 iDict
= CFDictionaryGetValue(interfaces
, iKey
);
317 iSIDs
= CFDictionaryGetValue(iDict
, kSCCachePropNetServiceIDs
);
318 for (k
= 0; iSIDs
&& k
< CFArrayGetCount(iSIDs
); k
++) {
319 iSID
= CFArrayGetValueAtIndex(iSIDs
, k
);
320 if (CFEqual(oSID
, iSID
)) {
327 /* if order ServiceID is associated with this interface */
328 CFArrayAppendValue(order
, iKey
);
329 CFArrayRemoveValueAtIndex(iKeys
, j
);
335 for (i
= 0; i
< CFArrayGetCount(iKeys
); i
++) {
338 iKey
= CFArrayGetValueAtIndex(iKeys
, i
);
339 CFArrayAppendValue(order
, iKey
);
348 getAddresses(CFDictionaryRef iDict
,
354 *addrs
= CFDictionaryGetValue(iDict
, kSCPropNetIPv4Addresses
);
355 *masks
= CFDictionaryGetValue(iDict
, kSCPropNetIPv4SubnetMasks
);
356 *dests
= CFDictionaryGetValue(iDict
, kSCPropNetIPv4DestAddresses
);
358 if ((*addrs
== NULL
) ||
359 ((*nAddrs
= CFArrayGetCount(*addrs
)) == 0)) {
360 /* sorry, no addresses */
364 if ((*masks
&& *dests
) ||
365 (*masks
== NULL
) && (*dests
== NULL
)) {
367 * sorry, we expect to have "SubnetMasks" or
368 * "DestAddresses" (not both) and if the count
369 * must match the number of "Addresses".
374 if (*masks
&& (*nAddrs
!= CFArrayGetCount(*masks
))) {
375 /* if we don't like the netmasks */
379 if (*dests
&& (*nAddrs
!= CFArrayGetCount(*dests
))) {
380 /* if we don't like the destaddresses */
388 checkAddress(SCDSessionRef session
,
389 const struct sockaddr
*address
,
391 CFDictionaryRef services
,
392 CFDictionaryRef interfaces
,
393 CFArrayRef interfaceOrder
,
394 struct in_addr
*defaultRoute
,
396 const char **errorMessage
)
401 CFStringRef iKey
= NULL
;
402 CFStringRef iType
= NULL
;
405 SCNStatus scn_status
= SCN_REACHABLE_UNKNOWN
;
407 CFMutableArrayRef sKeys
= NULL
;
408 CFStringRef sID
= NULL
;
409 CFArrayRef sIDs
= NULL
;
410 CFArrayRef sList
= NULL
;
412 CFStringRef sKey
= NULL
;
413 CFDictionaryRef sDict
= NULL
;
420 if (address
== NULL
) {
421 return SCN_REACHABLE_NO
;
424 sCnt
= CFDictionaryGetCount(services
);
425 keys
= CFAllocatorAllocate(NULL
, sCnt
* sizeof(CFStringRef
), 0);
426 CFDictionaryGetKeysAndValues(services
, keys
, NULL
);
427 tKeys
= CFArrayCreate(NULL
, keys
, sCnt
, &kCFTypeArrayCallBacks
);
428 CFAllocatorDeallocate(NULL
, keys
);
429 sKeys
= CFArrayCreateMutableCopy(NULL
, 0, tKeys
);
432 if (address
->sa_family
== AF_INET
) {
433 struct sockaddr_in
*sin
= (struct sockaddr_in
*)address
;
436 if (SCDOptionGet(session
, kSCDOptionDebug
))
437 SCDLog(LOG_INFO
, CFSTR("checkAddress(%s)"), inet_ntoa(sin
->sin_addr
));
440 * Check for loopback address
442 if (ntohl(sin
->sin_addr
.s_addr
) == ntohl(INADDR_LOOPBACK
)) {
443 /* if asking about the loopback address */
445 if (SCDOptionGet(session
, kSCDOptionDebug
))
446 SCDLog(LOG_INFO
, CFSTR(" isReachable via loopback"));
448 scn_status
= SCN_REACHABLE_YES
;
453 * Check if the address is on one of the subnets
454 * associated with our active IPv4 interfaces
456 iCnt
= CFArrayGetCount(interfaceOrder
);
457 for (i
=0; i
<iCnt
; i
++) {
460 CFDictionaryRef iDict
;
465 iKey
= CFArrayGetValueAtIndex(interfaceOrder
, i
);
466 iDict
= CFDictionaryGetValue(interfaces
, iKey
);
468 /* remove active services */
469 sIDs
= CFDictionaryGetValue(iDict
, kSCCachePropNetServiceIDs
);
470 for (j
= 0; sIDs
&& j
< CFArrayGetCount(sIDs
); j
++) {
474 sID
= CFArrayGetValueAtIndex(sIDs
, j
);
475 k
= CFArrayGetFirstIndexOfValue(sKeys
,
476 CFRangeMake(0, CFArrayGetCount(sKeys
)),
479 CFArrayRemoveValueAtIndex(sKeys
, k
);
483 if (!getAddresses(iDict
, &nAddrs
, &addrs
, &masks
, &dests
)) {
484 /* if no addresses to check */
488 for (j
=0; j
<nAddrs
; j
++) {
489 struct in_addr ifAddr
;
491 if (inet_atonCF(CFArrayGetValueAtIndex(addrs
, j
),
493 /* if Addresses string is invalid */
498 struct in_addr ifMask
;
500 if (inet_atonCF(CFArrayGetValueAtIndex(masks
, j
),
502 /* if SubnetMask string is invalid */
506 if ((ntohl(ifAddr
.s_addr
) & ntohl(ifMask
.s_addr
)) ==
507 (ntohl(sin
->sin_addr
.s_addr
) & ntohl(ifMask
.s_addr
))) {
508 /* the requested address is on this subnet */
510 if (SCDOptionGet(session
, kSCDOptionDebug
))
511 SCDLog(LOG_INFO
, CFSTR(" isReachable (my subnet)"));
513 scn_status
= SCN_REACHABLE_YES
;
517 struct in_addr destAddr
;
519 /* check remote address */
520 if (inet_atonCF(CFArrayGetValueAtIndex(dests
, j
),
522 /* if DestAddresses string is invalid */
526 /* check local address */
527 if (ntohl(sin
->sin_addr
.s_addr
) == ntohl(ifAddr
.s_addr
)) {
528 /* the address is our side of the link */
530 if (SCDOptionGet(session
, kSCDOptionDebug
))
531 SCDLog(LOG_INFO
, CFSTR(" isReachable (my local address)"));
533 scn_status
= SCN_REACHABLE_YES
;
537 if (ntohl(sin
->sin_addr
.s_addr
) == ntohl(destAddr
.s_addr
)) {
538 /* the address is the other side of the link */
540 if (SCDOptionGet(session
, kSCDOptionDebug
))
541 SCDLog(LOG_INFO
, CFSTR(" isReachable (my remote address)"));
543 scn_status
= SCN_REACHABLE_YES
;
551 * Check if the address is accessible via the "default" route.
553 for (i
=0; i
<iCnt
; i
++) {
556 CFDictionaryRef iDict
;
561 iKey
= CFArrayGetValueAtIndex(interfaceOrder
, i
);
562 iDict
= CFDictionaryGetValue(interfaces
, iKey
);
564 if (!getAddresses(iDict
, &nAddrs
, &addrs
, &masks
, &dests
)) {
565 /* if no addresses to check */
569 for (j
=0; defaultRoute
&& j
<nAddrs
; j
++) {
571 struct in_addr ifAddr
;
572 struct in_addr ifMask
;
574 if (inet_atonCF(CFArrayGetValueAtIndex(addrs
, j
),
576 /* if Addresses string is invalid */
580 if (inet_atonCF(CFArrayGetValueAtIndex(masks
, j
),
582 /* if SubnetMasks string is invalid */
586 if ((ntohl(ifAddr
.s_addr
) & ntohl(ifMask
.s_addr
)) ==
587 (ntohl(defaultRoute
->s_addr
) & ntohl(ifMask
.s_addr
))) {
588 /* the requested address is on this subnet */
590 if (SCDOptionGet(session
, kSCDOptionDebug
))
591 SCDLog(LOG_INFO
, CFSTR(" isReachable via default route (my subnet)"));
593 scn_status
= SCN_REACHABLE_YES
;
597 struct in_addr destAddr
;
599 /* check remote address */
600 if (inet_atonCF(CFArrayGetValueAtIndex(dests
, j
),
602 /* if DestAddresses string is invalid */
606 if (ntohl(destAddr
.s_addr
) == ntohl(defaultRoute
->s_addr
)) {
607 /* the address is the other side of the link */
609 if (SCDOptionGet(session
, kSCDOptionDebug
))
610 SCDLog(LOG_INFO
, CFSTR(" isReachable via default route (my remote address)"));
612 scn_status
= SCN_REACHABLE_YES
;
620 * Check the not active (but configured) IPv4 services
622 sCnt
= CFArrayGetCount(sKeys
);
623 for (i
=0; i
<sCnt
; i
++) {
625 CFStringRef configMethod
= NULL
;
631 sKey
= CFArrayGetValueAtIndex(sKeys
, i
);
632 sDict
= CFDictionaryGetValue(services
, sKey
);
635 * check configured network addresses
637 for (j
=0; j
<nAddrs
; j
++) {
638 struct in_addr ifAddr
;
640 if (inet_atonCF(CFArrayGetValueAtIndex(addrs
, j
),
642 /* if Addresses string is invalid */
647 struct in_addr ifMask
;
649 /* check address/netmask */
650 if (inet_atonCF(CFArrayGetValueAtIndex(masks
, j
),
652 /* if SubnetMasks string is invalid */
656 if ((ntohl(ifAddr
.s_addr
) & ntohl(ifMask
.s_addr
)) !=
657 (ntohl(sin
->sin_addr
.s_addr
) & ntohl(ifMask
.s_addr
))) {
658 /* the requested address is on this subnet */
660 if (SCDOptionGet(session
, kSCDOptionDebug
))
661 SCDLog(LOG_INFO
, CFSTR(" is configured w/static info (my subnet)"));
666 struct in_addr destAddr
;
668 /* check remote address */
669 if (inet_atonCF(CFArrayGetValueAtIndex(dests
, j
),
671 /* if DestAddresses string is invalid */
675 /* check local address */
676 if (ntohl(sin
->sin_addr
.s_addr
) == ntohl(ifAddr
.s_addr
)) {
677 /* the address is our side of the link */
679 if (SCDOptionGet(session
, kSCDOptionDebug
))
680 SCDLog(LOG_INFO
, CFSTR(" is configured w/static info (my local address)"));
685 if (ntohl(sin
->sin_addr
.s_addr
) == ntohl(destAddr
.s_addr
)) {
686 /* the address is the other side of the link */
688 if (SCDOptionGet(session
, kSCDOptionDebug
))
689 SCDLog(LOG_INFO
, CFSTR(" is configured w/static info (my remote address)"));
697 * check for dynamic (i.e. not manual) configuration
700 if (CFDictionaryGetValueIfPresent(sDict
,
701 kSCPropNetIPv4ConfigMethod
,
702 (void **)&configMethod
) &&
703 !CFEqual(configMethod
, kSCValNetIPv4ConfigMethodManual
)) {
704 /* if only we were "connected" */
706 if (SCDOptionGet(session
, kSCDOptionDebug
))
707 SCDLog(LOG_INFO
, CFSTR(" is configured w/dynamic addressing"));
714 if (SCDOptionGet(session
, kSCDOptionDebug
))
715 SCDLog(LOG_INFO
, CFSTR(" cannot be reached"));
717 scn_status
= SCN_REACHABLE_NO
;
722 * if no code for this address family (yet)
724 SCDSessionLog(session
,
726 CFSTR("checkAddress(): unexpected address family %d"),
728 if (errorMessage
!= NULL
) {
729 *errorMessage
= "unexpected address family";
739 * We have an interface which "claims" to be a valid path
740 * off of the system. Check to make sure that this isn't
741 * a dial-on-demand PPP link that isn't connected yet.
745 CFDictionaryRef sDict
;
747 /* attempt to get the interface type from the first service */
748 sID
= CFArrayGetValueAtIndex(sIDs
, 0);
749 sDict
= CFDictionaryGetValue(services
, sID
);
751 iType
= CFDictionaryGetValue(sDict
, kSCPropNetInterfaceType
);
755 /* if we don't know the interface type */
759 if (!CFEqual(iType
, kSCValNetInterfaceTypePPP
)) {
760 /* if not a ppp interface */
764 num
= CFDictionaryGetValue(sDict
, kSCPropNetPPPDialOnDemand
);
768 CFNumberGetValue(num
, kCFNumberIntType
, &dialOnDemand
);
769 if (flags
&& (dialOnDemand
!= 0)) {
770 *flags
|= kSCNFlagsConnectionAutomatic
;
774 } else if (!CFStringHasPrefix(iKey
, CFSTR("ppp"))) {
775 /* if not a ppp interface */
780 *flags
|= kSCNFlagsTransientConnection
;
785 struct ppp_status
*pppLinkStatus
;
789 * The service ID is available, ask the PPP controller
790 * for the extended status.
792 pppStatus
= PPPInit(&pppRef
);
793 if (pppStatus
!= 0) {
795 if (SCDOptionGet(session
, kSCDOptionDebug
))
796 SCDLog(LOG_DEBUG
, CFSTR(" PPPInit() failed: status=%d"), pppStatus
);
798 scn_status
= SCN_REACHABLE_UNKNOWN
;
799 if (errorMessage
!= NULL
) {
800 *errorMessage
= "PPPInit() failed";
805 pppStatus
= PPPGetLinkByServiceID(pppRef
, sID
, &pppLink
);
806 if (pppStatus
!= 0) {
808 if (SCDOptionGet(session
, kSCDOptionDebug
))
809 SCDLog(LOG_DEBUG
, CFSTR(" PPPGetLinkByServiceID() failed: status=%d"), pppStatus
);
811 scn_status
= SCN_REACHABLE_UNKNOWN
;
812 if (errorMessage
!= NULL
) {
813 *errorMessage
= "PPPGetLinkByServiceID() failed";
818 pppStatus
= PPPStatus(pppRef
, pppLink
, &pppLinkStatus
);
819 if (pppStatus
!= 0) {
821 if (SCDOptionGet(session
, kSCDOptionDebug
))
822 SCDLog(LOG_DEBUG
, CFSTR(" PPPStatus() failed: status=%d"), pppStatus
);
824 scn_status
= SCN_REACHABLE_UNKNOWN
;
825 if (errorMessage
!= NULL
) {
826 *errorMessage
= "PPPStatus() failed";
831 if (SCDOptionGet(session
, kSCDOptionDebug
))
832 SCDLog(LOG_DEBUG
, CFSTR(" PPP link status = %d"), pppLinkStatus
->status
);
834 switch (pppLinkStatus
->status
) {
836 /* if we're really UP and RUNNING */
839 /* if we're not connected at all */
841 if (SCDOptionGet(session
, kSCDOptionDebug
))
842 SCDLog(LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
844 scn_status
= SCN_REACHABLE_CONNECTION_REQUIRED
;
847 /* if we're in the process of [dis]connecting */
849 if (SCDOptionGet(session
, kSCDOptionDebug
))
850 SCDLog(LOG_INFO
, CFSTR(" PPP link, connection in progress"));
852 scn_status
= SCN_REACHABLE_CONNECTION_REQUIRED
;
855 CFAllocatorDeallocate(NULL
, pppLinkStatus
);
858 * The service ID is not available, check the interfaces
859 * UP and RUNNING flags.
861 bzero(&ifr
, sizeof(ifr
));
862 if (!CFStringGetCString(iKey
,
863 (char *)&ifr
.ifr_name
,
864 sizeof(ifr
.ifr_name
),
865 kCFStringEncodingMacRoman
)) {
866 scn_status
= SCN_REACHABLE_UNKNOWN
;
867 if (errorMessage
!= NULL
) {
868 *errorMessage
= "could not convert interface name to C string";
873 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
875 scn_status
= SCN_REACHABLE_UNKNOWN
;
876 if (errorMessage
!= NULL
) {
877 *errorMessage
= strerror(errno
);
882 if (ioctl(sock
, SIOCGIFFLAGS
, (caddr_t
)&ifr
) < 0) {
883 scn_status
= SCN_REACHABLE_UNKNOWN
;
884 if (errorMessage
!= NULL
) {
885 *errorMessage
= strerror(errno
);
891 if (SCDOptionGet(session
, kSCDOptionDebug
))
892 SCDLog(LOG_INFO
, CFSTR(" flags for %s == 0x%hx"), ifr
.ifr_name
, ifr
.ifr_flags
);
894 if ((ifr
.ifr_flags
& (IFF_UP
|IFF_RUNNING
)) != (IFF_UP
|IFF_RUNNING
)) {
895 if ((ifr
.ifr_flags
& IFF_UP
) == IFF_UP
) {
896 /* if we're "up" but not "running" */
898 if (SCDOptionGet(session
, kSCDOptionDebug
))
899 SCDLog(LOG_INFO
, CFSTR(" up & not running, dial-on-traffic to connect"));
901 scn_status
= SCN_REACHABLE_CONNECTION_REQUIRED
;
903 *flags
|= kSCNFlagsConnectionAutomatic
;
906 /* if we're not "up" and "running" */
908 if (SCDOptionGet(session
, kSCDOptionDebug
))
909 SCDLog(LOG_INFO
, CFSTR(" not up & running, connection required"));
911 scn_status
= SCN_REACHABLE_CONNECTION_REQUIRED
;
922 * We have a service which "claims" to be a potential path
923 * off of the system. Check to make sure that this is a
924 * type of PPP link before claiming it's viable.
927 CFDictionaryGetValueIfPresent(sDict
,
928 kSCPropNetInterfaceType
,
930 !CFEqual(iType
, kSCValNetInterfaceTypePPP
)) {
931 /* no path if this not a ppp interface */
933 if (SCDOptionGet(session
, kSCDOptionDebug
))
934 SCDLog(LOG_INFO
, CFSTR(" cannot be reached"));
936 scn_status
= SCN_REACHABLE_NO
;
940 scn_status
= SCN_REACHABLE_CONNECTION_REQUIRED
;
942 *flags
|= kSCNFlagsTransientConnection
;
947 if (sKeys
) CFRelease(sKeys
);
948 if (sList
) CFRelease(sList
);
949 if (pppRef
!= -1) (void) PPPDispose(pppRef
);
950 if (sock
!= -1) (void)close(sock
);
957 _IsReachableInit(SCDSessionRef session
,
958 CFDictionaryRef
*services
,
959 CFDictionaryRef
*interfaces
,
960 CFArrayRef
*interfaceOrder
,
961 struct in_addr
**defaultRoute
)
964 CFDictionaryRef dict
;
967 CFNumberRef pppOverridePrimary
= NULL
;
968 CFArrayRef serviceOrder
= NULL
;
969 struct in_addr
*route
= NULL
;
973 * get the ServiceOrder and PPPOverridePrimary keys
974 * from the global settings.
976 key
= SCDKeyCreateNetworkGlobalEntity(kSCCacheDomainSetup
, kSCEntNetIPv4
);
977 status
= SCDGet(session
, key
, &handle
);
981 /* if global settings are available */
982 dict
= SCDHandleGetData(handle
);
984 /* get service order */
985 if ((CFDictionaryGetValueIfPresent(dict
,
986 kSCPropNetServiceOrder
,
987 (void **)&serviceOrder
) == TRUE
)) {
988 CFRetain(serviceOrder
);
991 /* get PPP overrides primary flag */
992 if ((CFDictionaryGetValueIfPresent(dict
,
993 kSCPropNetPPPOverridePrimary
,
994 (void **)&pppOverridePrimary
) == TRUE
)) {
995 CFRetain(pppOverridePrimary
);
998 SCDHandleRelease(handle
);
1001 /* if no global settings */
1004 SCDLog(LOG_ERR
, CFSTR("SCDGet() failed: %s"), SCDError(status
));
1005 /* XXX need to do something more with this FATAL error XXXX */
1012 key
= SCDKeyCreateNetworkGlobalEntity(kSCCacheDomainState
,
1014 status
= SCDGet(session
, key
, &handle
);
1018 dict
= SCDHandleGetData(handle
);
1019 addr
= CFDictionaryGetValue(dict
, kSCPropNetIPv4Router
);
1021 /* if no default route */
1025 route
= CFAllocatorAllocate(NULL
, sizeof(struct in_addr
), 0);
1026 if (inet_atonCF(addr
, route
) == 0) {
1027 /* if address string is invalid */
1028 CFAllocatorDeallocate(NULL
, route
);
1032 *defaultRoute
= route
;
1036 /* if no default route */
1039 SCDSessionLog(session
,
1041 CFSTR("SCDGet() failed: %s"),
1046 SCDHandleRelease(handle
);
1051 * get the configured services and interfaces
1053 *services
= getServices (session
);
1054 *interfaces
= getInterfaces(session
);
1055 *interfaceOrder
= getInterfaceOrder(*interfaces
,
1057 pppOverridePrimary
);
1061 if (serviceOrder
) CFRelease(serviceOrder
);
1062 if (pppOverridePrimary
) CFRelease(pppOverridePrimary
);
1065 if (SCDOptionGet(session
, kSCDOptionDebug
)) {
1066 SCDLog(LOG_NOTICE
, CFSTR("interfaces = %@"), *interfaces
);
1067 SCDLog(LOG_NOTICE
, CFSTR("services = %@"), *services
);
1068 SCDLog(LOG_NOTICE
, CFSTR("interfaceOrder = %@"), *interfaceOrder
);
1069 SCDLog(LOG_NOTICE
, CFSTR("defaultRoute = %s"), *defaultRoute
?inet_ntoa(**defaultRoute
):"None");
1078 _IsReachableFree(CFDictionaryRef services
,
1079 CFDictionaryRef interfaces
,
1080 CFArrayRef interfaceOrder
,
1081 struct in_addr
*defaultRoute
)
1083 if (services
) CFRelease(services
);
1084 if (interfaces
) CFRelease(interfaces
);
1085 if (interfaceOrder
) CFRelease(interfaceOrder
);
1086 if (defaultRoute
) CFAllocatorDeallocate(NULL
, defaultRoute
);
1092 SCNIsReachableByAddress(const struct sockaddr
*address
,
1095 const char **errorMessage
)
1097 struct in_addr
*defaultRoute
= NULL
;
1098 CFDictionaryRef interfaces
= NULL
;
1099 CFArrayRef interfaceOrder
= NULL
;
1100 CFDictionaryRef services
= NULL
;
1101 SCDSessionRef session
= NULL
;
1102 SCDStatus scd_status
;
1103 SCNStatus scn_status
;
1105 scd_status
= SCDOpen(&session
, CFSTR("SCNIsReachableByAddress"));
1106 if (scd_status
!= SCD_OK
) {
1107 if (errorMessage
!= NULL
) {
1108 *errorMessage
= SCDError(scd_status
);
1110 return SCN_REACHABLE_UNKNOWN
;
1113 _IsReachableInit(session
, &services
, &interfaces
, &interfaceOrder
, &defaultRoute
);
1114 scn_status
= checkAddress(session
,
1123 _IsReachableFree(services
, interfaces
, interfaceOrder
, defaultRoute
);
1125 (void) SCDClose(&session
);
1131 SCNIsReachableByName(const char *nodename
,
1133 const char **errorMessage
)
1135 struct in_addr
*defaultRoute
= NULL
;
1138 CFDictionaryRef interfaces
= NULL
;
1139 CFArrayRef interfaceOrder
= NULL
;
1140 SCDStatus scd_status
= SCD_OK
;
1141 SCNStatus ns_status
= SCN_REACHABLE_YES
;
1142 struct addrinfo
*res
= NULL
;
1143 struct addrinfo
*resP
;
1144 CFDictionaryRef services
= NULL
;
1145 SCDSessionRef session
= NULL
;
1146 SCNStatus scn_status
= SCN_REACHABLE_YES
;
1148 scd_status
= SCDOpen(&session
, CFSTR("SCNIsReachableByName"));
1149 if (scd_status
!= SCD_OK
) {
1150 scn_status
= SCN_REACHABLE_UNKNOWN
;
1151 if (errorMessage
!= NULL
) {
1152 *errorMessage
= SCDError(scd_status
);
1157 _IsReachableInit(session
, &services
, &interfaces
, &interfaceOrder
, &defaultRoute
);
1160 * since we don't know which name server will be consulted
1161 * to resolve the specified nodename we need to check the
1162 * availability of ALL name servers.
1165 for (i
=0; i
<_res
.nscount
; i
++) {
1166 ns_status
= checkAddress(session
,
1167 (struct sockaddr
*)&_res
.nsaddr_list
[i
],
1168 _res
.nsaddr_list
[i
].sin_len
,
1175 if (ns_status
< scn_status
) {
1176 /* return the worst case result */
1177 scn_status
= ns_status
;
1178 if (ns_status
== SCN_REACHABLE_UNKNOWN
) {
1185 if (ns_status
< SCN_REACHABLE_YES
) {
1190 * OK, all of the DNS name servers are available. Let's
1191 * first assume that the requested host is NOT available,
1192 * resolve the nodename, and check its address for
1193 * accessibility. We return the best status available.
1195 scn_status
= SCN_REACHABLE_UNKNOWN
;
1198 * resolve the nodename into an address
1200 i
= getaddrinfo(nodename
, NULL
, NULL
, &res
);
1202 SCDSessionLog(session
,
1204 CFSTR("getaddrinfo() failed: %s"),
1209 for (resP
=res
; resP
!=NULL
; resP
=resP
->ai_next
) {
1210 ns_status
= checkAddress(session
,
1219 if (ns_status
> scn_status
) {
1220 /* return the best case result */
1221 scn_status
= ns_status
;
1222 if (ns_status
== SCN_REACHABLE_YES
) {
1234 * The getaddrinfo() function call didn't return any addresses. While
1235 * this may be the correct answer we have found that some DNS servers
1236 * may, depending on what has been cached, not return all available
1237 * records when issued a T_ANY query. To accomodate these servers
1238 * we double check by using the gethostbyname() function which uses
1239 * a simple T_A query.
1243 if (SCDOptionGet(session
, kSCDOptionDebug
))
1244 SCDLog(LOG_INFO
, CFSTR("getaddrinfo() returned no addresses, try gethostbyname()"));
1247 h
= gethostbyname(nodename
);
1248 if (h
&& h
->h_length
) {
1249 struct in_addr
**s
= (struct in_addr
**)h
->h_addr_list
;
1252 struct sockaddr_in sa
;
1254 bzero(&sa
, sizeof(sa
));
1255 sa
.sin_len
= sizeof(sa
);
1256 sa
.sin_family
= AF_INET
;
1259 ns_status
= checkAddress(session
,
1260 (struct sockaddr
*)&sa
,
1268 if (ns_status
> scn_status
) {
1269 /* return the best case result */
1270 scn_status
= ns_status
;
1271 if (ns_status
== SCN_REACHABLE_YES
) {
1283 _IsReachableFree(services
, interfaces
, interfaceOrder
, defaultRoute
);
1284 if (session
) (void)SCDClose(&session
);
1285 if (res
) freeaddrinfo(res
);