]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetwork.c
configd-24.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetwork.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <SystemConfiguration/SystemConfiguration.h>
24
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <arpa/nameser.h>
28 #include <netdb.h>
29 #include <resolv.h>
30 #include <unistd.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
33 #include <net/if.h>
34
35 #include "ppp.h"
36
37
38 static int
39 inet_atonCF(CFStringRef cfStr, struct in_addr *addr)
40 {
41 char cStr[sizeof("255.255.255.255")];
42
43 if (!CFStringGetCString(cfStr, cStr, sizeof(cStr), kCFStringEncodingMacRoman)) {
44 return 0;
45 }
46
47 return inet_aton(cStr, addr);
48 }
49
50
51 /*
52 * Function: parse_component
53 * Purpose:
54 * Given a string 'key' and a string prefix 'prefix',
55 * return the next component in the slash '/' separated
56 * key.
57 *
58 * Examples:
59 * 1. key = "a/b/c" prefix = "a/"
60 * returns "b"
61 * 2. key = "a/b/c" prefix = "a/b/"
62 * returns "c"
63 */
64 static CFStringRef
65 parse_component(CFStringRef key, CFStringRef prefix)
66 {
67 CFMutableStringRef comp;
68 CFRange range;
69
70 if (CFStringHasPrefix(key, prefix) == FALSE) {
71 return NULL;
72 }
73 comp = CFStringCreateMutableCopy(NULL, 0, key);
74 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
75 range = CFStringFind(comp, CFSTR("/"), 0);
76 if (range.location == kCFNotFound) {
77 return comp;
78 }
79 range.length = CFStringGetLength(comp) - range.location;
80 CFStringDelete(comp, range);
81 return comp;
82 }
83
84
85 /*
86 * return a dictionary of configured services.
87 */
88 static CFDictionaryRef
89 getServices(SCDSessionRef session)
90 {
91 CFArrayRef defined = NULL;
92 int i;
93 CFStringRef key;
94 CFStringRef prefix;
95 CFMutableDictionaryRef services;
96 SCDStatus status;
97
98 prefix = SCDKeyCreate(CFSTR("%@/%@/%@/"),
99 kSCCacheDomainSetup,
100 kSCCompNetwork,
101 kSCCompService);
102
103 services = CFDictionaryCreateMutable(NULL,
104 0,
105 &kCFTypeDictionaryKeyCallBacks,
106 &kCFTypeDictionaryValueCallBacks);
107
108 key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
109 kSCCompAnyRegex,
110 kSCEntNetIPv4);
111 status = SCDList(session, key, kSCDRegexKey, &defined);
112 CFRelease(key);
113 if (status != SCD_OK) {
114 goto done;
115 }
116
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;
127
128 key = CFArrayGetValueAtIndex(defined, i);
129
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 */
134 goto nextService;
135 }
136 ip_dict = SCDHandleGetData(ip_handle);
137
138 sDict = CFDictionaryCreateMutableCopy(NULL, 0, ip_dict);
139
140 /* add keys from the service's Interface dictionary */
141 sid = parse_component(key, prefix);
142 if (sid == NULL) {
143 goto nextService;
144 }
145
146 key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
147 sid,
148 kSCEntNetInterface);
149 status = SCDGet(session, key, &if_handle);
150 CFRelease(key);
151 if (status != SCD_OK) {
152 goto nextService;
153 }
154 if_dict = SCDHandleGetData(if_handle);
155
156 /* check the interface "Type", "SubType", and "DeviceName" */
157 if (CFDictionaryGetValueIfPresent(if_dict,
158 kSCPropNetInterfaceType,
159 (void **)&key)) {
160 CFDictionaryAddValue(sDict, kSCPropNetInterfaceType, key);
161 isPPP = CFEqual(key, kSCValNetInterfaceTypePPP);
162 }
163 if (CFDictionaryGetValueIfPresent(if_dict,
164 kSCPropNetInterfaceSubType,
165 (void **)&key)) {
166 CFDictionaryAddValue(sDict, kSCPropNetInterfaceSubType, key);
167 }
168
169 if (isPPP) {
170 key = SCDKeyCreateNetworkServiceEntity(kSCCacheDomainSetup,
171 sid,
172 kSCEntNetPPP);
173 status = SCDGet(session, key, &ppp_handle);
174 CFRelease(key);
175 if (status != SCD_OK) {
176 goto nextService;
177 }
178 ppp_dict = SCDHandleGetData(ppp_handle);
179
180 /* get Dial-on-Traffic flag */
181 if (CFDictionaryGetValueIfPresent(ppp_dict,
182 kSCPropNetPPPDialOnDemand,
183 (void **)&key)) {
184 CFDictionaryAddValue(sDict, kSCPropNetPPPDialOnDemand, key);
185 }
186 }
187
188 CFDictionaryAddValue(services, sid, sDict);
189
190 nextService:
191
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);
197 }
198
199 done:
200
201 if (defined) CFRelease(defined);
202 CFRelease(prefix);
203
204 return services;
205 }
206
207
208 /*
209 * return a dictionary of configured interfaces.
210 */
211 static CFDictionaryRef
212 getInterfaces(SCDSessionRef session)
213 {
214 CFMutableArrayRef defined = NULL;
215 int i;
216 CFStringRef key;
217 CFMutableDictionaryRef interfaces;
218 CFStringRef prefix;
219 SCDStatus status;
220
221 prefix = SCDKeyCreate(CFSTR("%@/%@/%@/"),
222 kSCCacheDomainState,
223 kSCCompNetwork,
224 kSCCompInterface);
225
226 interfaces = CFDictionaryCreateMutable(NULL,
227 0,
228 &kCFTypeDictionaryKeyCallBacks,
229 &kCFTypeDictionaryValueCallBacks);
230
231 key = SCDKeyCreateNetworkInterfaceEntity(kSCCacheDomainState,
232 kSCCompAnyRegex,
233 kSCEntNetIPv4);
234 status = SCDList(session, key, kSCDRegexKey, &defined);
235 CFRelease(key);
236 if (status != SCD_OK) {
237 goto done;
238 }
239
240 for (i=0; i<CFArrayGetCount(defined); i++) {
241 CFStringRef iid = NULL;
242 CFDictionaryRef ip_dict;
243 SCDHandleRef ip_handle = NULL;
244
245 key = CFArrayGetValueAtIndex(defined, i);
246
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 */
251 goto nextIF;
252 }
253 ip_dict = SCDHandleGetData(ip_handle);
254
255 iid = parse_component(key, prefix);
256 if (iid == NULL) {
257 goto nextIF;
258 }
259
260 CFDictionaryAddValue(interfaces, iid, ip_dict);
261
262 nextIF :
263
264 if (iid) CFRelease(iid);
265 if (ip_handle) SCDHandleRelease(ip_handle);
266 }
267
268 done:
269
270 if (defined) CFRelease(defined);
271 CFRelease(prefix);
272 return interfaces;
273 }
274
275
276 /*
277 * return an array of interface names based on a specified service order.
278 */
279 static CFArrayRef
280 getInterfaceOrder(CFDictionaryRef interfaces,
281 CFArrayRef serviceOrder,
282 CFNumberRef pppOverridePrimary)
283 {
284 CFIndex i;
285 CFIndex iCnt;
286 CFMutableArrayRef iKeys;
287 void **keys;
288 CFMutableArrayRef order = NULL;
289 CFArrayRef tKeys;
290
291 order = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
292
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);
299 CFRelease(tKeys);
300
301 for (i = 0; serviceOrder && i < CFArrayGetCount(serviceOrder); i++) {
302 CFIndex j;
303 CFStringRef oSID;
304
305 oSID = CFArrayGetValueAtIndex(serviceOrder, i);
306 for (j=0; j<CFArrayGetCount(iKeys); j++) {
307 CFDictionaryRef iDict;
308 CFStringRef iKey;
309 CFStringRef iSID;
310 CFArrayRef iSIDs;
311 CFIndex k;
312 boolean_t match = FALSE;
313
314 iKey = CFArrayGetValueAtIndex(iKeys, j);
315 iDict = CFDictionaryGetValue(interfaces, iKey);
316
317 iSIDs = CFDictionaryGetValue(iDict, kSCCachePropNetServiceIDs);
318 for (k = 0; iSIDs && k < CFArrayGetCount(iSIDs); k++) {
319 iSID = CFArrayGetValueAtIndex(iSIDs, k);
320 if (CFEqual(oSID, iSID)) {
321 match = TRUE;
322 break;
323 }
324 }
325
326 if (match) {
327 /* if order ServiceID is associated with this interface */
328 CFArrayAppendValue(order, iKey);
329 CFArrayRemoveValueAtIndex(iKeys, j);
330 break;
331 }
332 }
333 }
334
335 for (i = 0; i < CFArrayGetCount(iKeys); i++) {
336 CFStringRef iKey;
337
338 iKey = CFArrayGetValueAtIndex(iKeys, i);
339 CFArrayAppendValue(order, iKey);
340 }
341
342 CFRelease(iKeys);
343 return order;
344 }
345
346
347 static boolean_t
348 getAddresses(CFDictionaryRef iDict,
349 CFIndex *nAddrs,
350 CFArrayRef *addrs,
351 CFArrayRef *masks,
352 CFArrayRef *dests)
353 {
354 *addrs = CFDictionaryGetValue(iDict, kSCPropNetIPv4Addresses);
355 *masks = CFDictionaryGetValue(iDict, kSCPropNetIPv4SubnetMasks);
356 *dests = CFDictionaryGetValue(iDict, kSCPropNetIPv4DestAddresses);
357
358 if ((*addrs == NULL) ||
359 ((*nAddrs = CFArrayGetCount(*addrs)) == 0)) {
360 /* sorry, no addresses */
361 return FALSE;
362 }
363
364 if ((*masks && *dests) ||
365 (*masks == NULL) && (*dests == NULL)) {
366 /*
367 * sorry, we expect to have "SubnetMasks" or
368 * "DestAddresses" (not both) and if the count
369 * must match the number of "Addresses".
370 */
371 return FALSE;
372 }
373
374 if (*masks && (*nAddrs != CFArrayGetCount(*masks))) {
375 /* if we don't like the netmasks */
376 return FALSE;
377 }
378
379 if (*dests && (*nAddrs != CFArrayGetCount(*dests))) {
380 /* if we don't like the destaddresses */
381 return FALSE;
382 }
383
384 return TRUE;
385 }
386
387 static SCNStatus
388 checkAddress(SCDSessionRef session,
389 const struct sockaddr *address,
390 const int addrlen,
391 CFDictionaryRef services,
392 CFDictionaryRef interfaces,
393 CFArrayRef interfaceOrder,
394 struct in_addr *defaultRoute,
395 int *flags,
396 const char **errorMessage)
397 {
398 CFIndex i;
399 struct ifreq ifr;
400 CFIndex iCnt;
401 CFStringRef iKey = NULL;
402 CFStringRef iType = NULL;
403 void **keys;
404 int pppRef = -1;
405 SCNStatus scn_status = SCN_REACHABLE_UNKNOWN;
406 CFIndex sCnt;
407 CFMutableArrayRef sKeys = NULL;
408 CFStringRef sID = NULL;
409 CFArrayRef sIDs = NULL;
410 CFArrayRef sList = NULL;
411 int sock = -1;
412 CFStringRef sKey = NULL;
413 CFDictionaryRef sDict = NULL;
414 CFArrayRef tKeys;
415
416 if (flags != NULL) {
417 *flags = 0;
418 }
419
420 if (address == NULL) {
421 return SCN_REACHABLE_NO;
422 }
423
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);
430 CFRelease(tKeys);
431
432 if (address->sa_family == AF_INET) {
433 struct sockaddr_in *sin = (struct sockaddr_in *)address;
434
435 #ifdef DEBUG
436 if (SCDOptionGet(session, kSCDOptionDebug))
437 SCDLog(LOG_INFO, CFSTR("checkAddress(%s)"), inet_ntoa(sin->sin_addr));
438 #endif /* DEBUG */
439 /*
440 * Check for loopback address
441 */
442 if (ntohl(sin->sin_addr.s_addr) == ntohl(INADDR_LOOPBACK)) {
443 /* if asking about the loopback address */
444 #ifdef DEBUG
445 if (SCDOptionGet(session, kSCDOptionDebug))
446 SCDLog(LOG_INFO, CFSTR(" isReachable via loopback"));
447 #endif /* DEBUG */
448 scn_status = SCN_REACHABLE_YES;
449 goto done;
450 }
451
452 /*
453 * Check if the address is on one of the subnets
454 * associated with our active IPv4 interfaces
455 */
456 iCnt = CFArrayGetCount(interfaceOrder);
457 for (i=0; i<iCnt; i++) {
458 CFArrayRef addrs;
459 CFArrayRef dests;
460 CFDictionaryRef iDict;
461 CFIndex j;
462 CFArrayRef masks;
463 CFIndex nAddrs = 0;
464
465 iKey = CFArrayGetValueAtIndex(interfaceOrder, i);
466 iDict = CFDictionaryGetValue(interfaces, iKey);
467
468 /* remove active services */
469 sIDs = CFDictionaryGetValue(iDict, kSCCachePropNetServiceIDs);
470 for (j = 0; sIDs && j < CFArrayGetCount(sIDs); j++) {
471 CFIndex k;
472 CFStringRef sID;
473
474 sID = CFArrayGetValueAtIndex(sIDs, j);
475 k = CFArrayGetFirstIndexOfValue(sKeys,
476 CFRangeMake(0, CFArrayGetCount(sKeys)),
477 sID);
478 if (k != -1) {
479 CFArrayRemoveValueAtIndex(sKeys, k);
480 }
481 }
482
483 if (!getAddresses(iDict, &nAddrs, &addrs, &masks, &dests)) {
484 /* if no addresses to check */
485 continue;
486 }
487
488 for (j=0; j<nAddrs; j++) {
489 struct in_addr ifAddr;
490
491 if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
492 &ifAddr) == 0) {
493 /* if Addresses string is invalid */
494 break;
495 }
496
497 if (masks) {
498 struct in_addr ifMask;
499
500 if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
501 &ifMask) == 0) {
502 /* if SubnetMask string is invalid */
503 break;
504 }
505
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 */
509 #ifdef DEBUG
510 if (SCDOptionGet(session, kSCDOptionDebug))
511 SCDLog(LOG_INFO, CFSTR(" isReachable (my subnet)"));
512 #endif /* DEBUG */
513 scn_status = SCN_REACHABLE_YES;
514 goto checkInterface;
515 }
516 } else {
517 struct in_addr destAddr;
518
519 /* check remote address */
520 if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
521 &destAddr) == 0) {
522 /* if DestAddresses string is invalid */
523 break;
524 }
525
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 */
529 #ifdef DEBUG
530 if (SCDOptionGet(session, kSCDOptionDebug))
531 SCDLog(LOG_INFO, CFSTR(" isReachable (my local address)"));
532 #endif /* DEBUG */
533 scn_status = SCN_REACHABLE_YES;
534 goto checkInterface;
535 }
536
537 if (ntohl(sin->sin_addr.s_addr) == ntohl(destAddr.s_addr)) {
538 /* the address is the other side of the link */
539 #ifdef DEBUG
540 if (SCDOptionGet(session, kSCDOptionDebug))
541 SCDLog(LOG_INFO, CFSTR(" isReachable (my remote address)"));
542 #endif /* DEBUG */
543 scn_status = SCN_REACHABLE_YES;
544 goto checkInterface;
545 }
546 }
547 }
548 }
549
550 /*
551 * Check if the address is accessible via the "default" route.
552 */
553 for (i=0; i<iCnt; i++) {
554 CFArrayRef addrs;
555 CFArrayRef dests;
556 CFDictionaryRef iDict;
557 CFIndex j;
558 CFArrayRef masks;
559 CFIndex nAddrs = 0;
560
561 iKey = CFArrayGetValueAtIndex(interfaceOrder, i);
562 iDict = CFDictionaryGetValue(interfaces, iKey);
563
564 if (!getAddresses(iDict, &nAddrs, &addrs, &masks, &dests)) {
565 /* if no addresses to check */
566 continue;
567 }
568
569 for (j=0; defaultRoute && j<nAddrs; j++) {
570 if (masks) {
571 struct in_addr ifAddr;
572 struct in_addr ifMask;
573
574 if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
575 &ifAddr) == 0) {
576 /* if Addresses string is invalid */
577 break;
578 }
579
580 if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
581 &ifMask) == 0) {
582 /* if SubnetMasks string is invalid */
583 break;
584 }
585
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 */
589 #ifdef DEBUG
590 if (SCDOptionGet(session, kSCDOptionDebug))
591 SCDLog(LOG_INFO, CFSTR(" isReachable via default route (my subnet)"));
592 #endif /* DEBUG */
593 scn_status = SCN_REACHABLE_YES;
594 goto checkInterface;
595 }
596 } else {
597 struct in_addr destAddr;
598
599 /* check remote address */
600 if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
601 &destAddr) == 0) {
602 /* if DestAddresses string is invalid */
603 break;
604 }
605
606 if (ntohl(destAddr.s_addr) == ntohl(defaultRoute->s_addr)) {
607 /* the address is the other side of the link */
608 #ifdef DEBUG
609 if (SCDOptionGet(session, kSCDOptionDebug))
610 SCDLog(LOG_INFO, CFSTR(" isReachable via default route (my remote address)"));
611 #endif /* DEBUG */
612 scn_status = SCN_REACHABLE_YES;
613 goto checkInterface;
614 }
615 }
616 }
617 }
618
619 /*
620 * Check the not active (but configured) IPv4 services
621 */
622 sCnt = CFArrayGetCount(sKeys);
623 for (i=0; i<sCnt; i++) {
624 CFArrayRef addrs;
625 CFStringRef configMethod = NULL;
626 CFArrayRef dests;
627 CFIndex j;
628 CFArrayRef masks;
629 CFIndex nAddrs = 0;
630
631 sKey = CFArrayGetValueAtIndex(sKeys, i);
632 sDict = CFDictionaryGetValue(services, sKey);
633
634 /*
635 * check configured network addresses
636 */
637 for (j=0; j<nAddrs; j++) {
638 struct in_addr ifAddr;
639
640 if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
641 &ifAddr) == 0) {
642 /* if Addresses string is invalid */
643 break;
644 }
645
646 if (masks) {
647 struct in_addr ifMask;
648
649 /* check address/netmask */
650 if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
651 &ifMask) == 0) {
652 /* if SubnetMasks string is invalid */
653 break;
654 }
655
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 */
659 #ifdef DEBUG
660 if (SCDOptionGet(session, kSCDOptionDebug))
661 SCDLog(LOG_INFO, CFSTR(" is configured w/static info (my subnet)"));
662 #endif /* DEBUG */
663 goto checkService;
664 }
665 } else {
666 struct in_addr destAddr;
667
668 /* check remote address */
669 if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
670 &destAddr) == 0) {
671 /* if DestAddresses string is invalid */
672 break;
673 }
674
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 */
678 #ifdef DEBUG
679 if (SCDOptionGet(session, kSCDOptionDebug))
680 SCDLog(LOG_INFO, CFSTR(" is configured w/static info (my local address)"));
681 #endif /* DEBUG */
682 goto checkService;
683 }
684
685 if (ntohl(sin->sin_addr.s_addr) == ntohl(destAddr.s_addr)) {
686 /* the address is the other side of the link */
687 #ifdef DEBUG
688 if (SCDOptionGet(session, kSCDOptionDebug))
689 SCDLog(LOG_INFO, CFSTR(" is configured w/static info (my remote address)"));
690 #endif /* DEBUG */
691 goto checkService;
692 }
693 }
694 }
695
696 /*
697 * check for dynamic (i.e. not manual) configuration
698 * method.
699 */
700 if (CFDictionaryGetValueIfPresent(sDict,
701 kSCPropNetIPv4ConfigMethod,
702 (void **)&configMethod) &&
703 !CFEqual(configMethod, kSCValNetIPv4ConfigMethodManual)) {
704 /* if only we were "connected" */
705 #ifdef DEBUG
706 if (SCDOptionGet(session, kSCDOptionDebug))
707 SCDLog(LOG_INFO, CFSTR(" is configured w/dynamic addressing"));
708 #endif /* DEBUG */
709 goto checkService;
710 }
711 }
712
713 #ifdef DEBUG
714 if (SCDOptionGet(session, kSCDOptionDebug))
715 SCDLog(LOG_INFO, CFSTR(" cannot be reached"));
716 #endif /* DEBUG */
717 scn_status = SCN_REACHABLE_NO;
718 goto done;
719
720 } else {
721 /*
722 * if no code for this address family (yet)
723 */
724 SCDSessionLog(session,
725 LOG_ERR,
726 CFSTR("checkAddress(): unexpected address family %d"),
727 address->sa_family);
728 if (errorMessage != NULL) {
729 *errorMessage = "unexpected address family";
730 }
731 goto done;
732 }
733
734 goto done;
735
736 checkInterface :
737
738 /*
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.
742 */
743 if (sIDs) {
744 CFNumberRef num;
745 CFDictionaryRef sDict;
746
747 /* attempt to get the interface type from the first service */
748 sID = CFArrayGetValueAtIndex(sIDs, 0);
749 sDict = CFDictionaryGetValue(services, sID);
750 if (sDict) {
751 iType = CFDictionaryGetValue(sDict, kSCPropNetInterfaceType);
752 }
753
754 if (!iType) {
755 /* if we don't know the interface type */
756 goto done;
757 }
758
759 if (!CFEqual(iType, kSCValNetInterfaceTypePPP)) {
760 /* if not a ppp interface */
761 goto done;
762 }
763
764 num = CFDictionaryGetValue(sDict, kSCPropNetPPPDialOnDemand);
765 if (num) {
766 int dialOnDemand;
767
768 CFNumberGetValue(num, kCFNumberIntType, &dialOnDemand);
769 if (flags && (dialOnDemand != 0)) {
770 *flags |= kSCNFlagsConnectionAutomatic;
771 }
772
773 }
774 } else if (!CFStringHasPrefix(iKey, CFSTR("ppp"))) {
775 /* if not a ppp interface */
776 goto done;
777 }
778
779 if (flags != NULL) {
780 *flags |= kSCNFlagsTransientConnection;
781 }
782
783 if (sID) {
784 u_int32_t pppLink;
785 struct ppp_status *pppLinkStatus;
786 int pppStatus;
787
788 /*
789 * The service ID is available, ask the PPP controller
790 * for the extended status.
791 */
792 pppStatus = PPPInit(&pppRef);
793 if (pppStatus != 0) {
794 #ifdef DEBUG
795 if (SCDOptionGet(session, kSCDOptionDebug))
796 SCDLog(LOG_DEBUG, CFSTR(" PPPInit() failed: status=%d"), pppStatus);
797 #endif /* DEBUG */
798 scn_status = SCN_REACHABLE_UNKNOWN;
799 if (errorMessage != NULL) {
800 *errorMessage = "PPPInit() failed";
801 }
802 goto done;
803 }
804
805 pppStatus = PPPGetLinkByServiceID(pppRef, sID, &pppLink);
806 if (pppStatus != 0) {
807 #ifdef DEBUG
808 if (SCDOptionGet(session, kSCDOptionDebug))
809 SCDLog(LOG_DEBUG, CFSTR(" PPPGetLinkByServiceID() failed: status=%d"), pppStatus);
810 #endif /* DEBUG */
811 scn_status = SCN_REACHABLE_UNKNOWN;
812 if (errorMessage != NULL) {
813 *errorMessage = "PPPGetLinkByServiceID() failed";
814 }
815 goto done;
816 }
817
818 pppStatus = PPPStatus(pppRef, pppLink, &pppLinkStatus);
819 if (pppStatus != 0) {
820 #ifdef DEBUG
821 if (SCDOptionGet(session, kSCDOptionDebug))
822 SCDLog(LOG_DEBUG, CFSTR(" PPPStatus() failed: status=%d"), pppStatus);
823 #endif /* DEBUG */
824 scn_status = SCN_REACHABLE_UNKNOWN;
825 if (errorMessage != NULL) {
826 *errorMessage = "PPPStatus() failed";
827 }
828 goto done;
829 }
830 #ifdef DEBUG
831 if (SCDOptionGet(session, kSCDOptionDebug))
832 SCDLog(LOG_DEBUG, CFSTR(" PPP link status = %d"), pppLinkStatus->status);
833 #endif /* DEBUG */
834 switch (pppLinkStatus->status) {
835 case PPP_RUNNING :
836 /* if we're really UP and RUNNING */
837 break;
838 case PPP_IDLE :
839 /* if we're not connected at all */
840 #ifdef DEBUG
841 if (SCDOptionGet(session, kSCDOptionDebug))
842 SCDLog(LOG_INFO, CFSTR(" PPP link idle, dial-on-traffic to connect"));
843 #endif /* DEBUG */
844 scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
845 break;
846 default :
847 /* if we're in the process of [dis]connecting */
848 #ifdef DEBUG
849 if (SCDOptionGet(session, kSCDOptionDebug))
850 SCDLog(LOG_INFO, CFSTR(" PPP link, connection in progress"));
851 #endif /* DEBUG */
852 scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
853 break;
854 }
855 CFAllocatorDeallocate(NULL, pppLinkStatus);
856 } else {
857 /*
858 * The service ID is not available, check the interfaces
859 * UP and RUNNING flags.
860 */
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";
869 }
870 goto done;
871 }
872
873 sock = socket(AF_INET, SOCK_DGRAM, 0);
874 if (sock == -1) {
875 scn_status = SCN_REACHABLE_UNKNOWN;
876 if (errorMessage != NULL) {
877 *errorMessage = strerror(errno);
878 }
879 goto done;
880 }
881
882 if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
883 scn_status = SCN_REACHABLE_UNKNOWN;
884 if (errorMessage != NULL) {
885 *errorMessage = strerror(errno);
886 }
887 goto done;
888 }
889
890 #ifdef DEBUG
891 if (SCDOptionGet(session, kSCDOptionDebug))
892 SCDLog(LOG_INFO, CFSTR(" flags for %s == 0x%hx"), ifr.ifr_name, ifr.ifr_flags);
893 #endif /* DEBUG */
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" */
897 #ifdef DEBUG
898 if (SCDOptionGet(session, kSCDOptionDebug))
899 SCDLog(LOG_INFO, CFSTR(" up & not running, dial-on-traffic to connect"));
900 #endif /* DEBUG */
901 scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
902 if (flags != NULL) {
903 *flags |= kSCNFlagsConnectionAutomatic;
904 }
905 } else {
906 /* if we're not "up" and "running" */
907 #ifdef DEBUG
908 if (SCDOptionGet(session, kSCDOptionDebug))
909 SCDLog(LOG_INFO, CFSTR(" not up & running, connection required"));
910 #endif /* DEBUG */
911 scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
912 }
913 goto done;
914 }
915 }
916
917 goto done;
918
919 checkService :
920
921 /*
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.
925 */
926 if (sDict &&
927 CFDictionaryGetValueIfPresent(sDict,
928 kSCPropNetInterfaceType,
929 (void **)&iType) &&
930 !CFEqual(iType, kSCValNetInterfaceTypePPP)) {
931 /* no path if this not a ppp interface */
932 #ifdef DEBUG
933 if (SCDOptionGet(session, kSCDOptionDebug))
934 SCDLog(LOG_INFO, CFSTR(" cannot be reached"));
935 #endif /* DEBUG */
936 scn_status = SCN_REACHABLE_NO;
937 goto done;
938 }
939
940 scn_status = SCN_REACHABLE_CONNECTION_REQUIRED;
941 if (flags != NULL) {
942 *flags |= kSCNFlagsTransientConnection;
943 }
944
945 done :
946
947 if (sKeys) CFRelease(sKeys);
948 if (sList) CFRelease(sList);
949 if (pppRef != -1) (void) PPPDispose(pppRef);
950 if (sock != -1) (void)close(sock);
951
952 return scn_status;
953 }
954
955
956 static void
957 _IsReachableInit(SCDSessionRef session,
958 CFDictionaryRef *services,
959 CFDictionaryRef *interfaces,
960 CFArrayRef *interfaceOrder,
961 struct in_addr **defaultRoute)
962 {
963 CFStringRef addr;
964 CFDictionaryRef dict;
965 CFStringRef key;
966 SCDHandleRef handle;
967 CFNumberRef pppOverridePrimary = NULL;
968 CFArrayRef serviceOrder = NULL;
969 struct in_addr *route = NULL;
970 SCDStatus status;
971
972 /*
973 * get the ServiceOrder and PPPOverridePrimary keys
974 * from the global settings.
975 */
976 key = SCDKeyCreateNetworkGlobalEntity(kSCCacheDomainSetup, kSCEntNetIPv4);
977 status = SCDGet(session, key, &handle);
978 CFRelease(key);
979 switch (status) {
980 case SCD_OK :
981 /* if global settings are available */
982 dict = SCDHandleGetData(handle);
983
984 /* get service order */
985 if ((CFDictionaryGetValueIfPresent(dict,
986 kSCPropNetServiceOrder,
987 (void **)&serviceOrder) == TRUE)) {
988 CFRetain(serviceOrder);
989 }
990
991 /* get PPP overrides primary flag */
992 if ((CFDictionaryGetValueIfPresent(dict,
993 kSCPropNetPPPOverridePrimary,
994 (void **)&pppOverridePrimary) == TRUE)) {
995 CFRetain(pppOverridePrimary);
996 }
997
998 SCDHandleRelease(handle);
999 break;
1000 case SCD_NOKEY :
1001 /* if no global settings */
1002 break;
1003 default :
1004 SCDLog(LOG_ERR, CFSTR("SCDGet() failed: %s"), SCDError(status));
1005 /* XXX need to do something more with this FATAL error XXXX */
1006 goto error;
1007 }
1008
1009 /*
1010 * Get default route
1011 */
1012 key = SCDKeyCreateNetworkGlobalEntity(kSCCacheDomainState,
1013 kSCEntNetIPv4);
1014 status = SCDGet(session, key, &handle);
1015 CFRelease(key);
1016 switch (status) {
1017 case SCD_OK :
1018 dict = SCDHandleGetData(handle);
1019 addr = CFDictionaryGetValue(dict, kSCPropNetIPv4Router);
1020 if (addr == NULL) {
1021 /* if no default route */
1022 break;
1023 }
1024
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);
1029 route = NULL;
1030 break;
1031 }
1032 *defaultRoute = route;
1033
1034 break;
1035 case SCD_NOKEY :
1036 /* if no default route */
1037 break;
1038 default :
1039 SCDSessionLog(session,
1040 LOG_ERR,
1041 CFSTR("SCDGet() failed: %s"),
1042 SCDError(status));
1043 goto error;
1044 }
1045 if (handle) {
1046 SCDHandleRelease(handle);
1047 handle = NULL;
1048 }
1049
1050 /*
1051 * get the configured services and interfaces
1052 */
1053 *services = getServices (session);
1054 *interfaces = getInterfaces(session);
1055 *interfaceOrder = getInterfaceOrder(*interfaces,
1056 serviceOrder,
1057 pppOverridePrimary);
1058
1059 error :
1060
1061 if (serviceOrder) CFRelease(serviceOrder);
1062 if (pppOverridePrimary) CFRelease(pppOverridePrimary);
1063
1064 #ifdef DEBUG
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");
1070 }
1071 #endif /* DEBUG */
1072 return;
1073
1074 }
1075
1076
1077 static void
1078 _IsReachableFree(CFDictionaryRef services,
1079 CFDictionaryRef interfaces,
1080 CFArrayRef interfaceOrder,
1081 struct in_addr *defaultRoute)
1082 {
1083 if (services) CFRelease(services);
1084 if (interfaces) CFRelease(interfaces);
1085 if (interfaceOrder) CFRelease(interfaceOrder);
1086 if (defaultRoute) CFAllocatorDeallocate(NULL, defaultRoute);
1087 return;
1088 }
1089
1090
1091 SCNStatus
1092 SCNIsReachableByAddress(const struct sockaddr *address,
1093 const int addrlen,
1094 int *flags,
1095 const char **errorMessage)
1096 {
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;
1104
1105 scd_status = SCDOpen(&session, CFSTR("SCNIsReachableByAddress"));
1106 if (scd_status != SCD_OK) {
1107 if (errorMessage != NULL) {
1108 *errorMessage = SCDError(scd_status);
1109 }
1110 return SCN_REACHABLE_UNKNOWN;
1111 }
1112
1113 _IsReachableInit(session, &services, &interfaces, &interfaceOrder, &defaultRoute);
1114 scn_status = checkAddress(session,
1115 address,
1116 addrlen,
1117 services,
1118 interfaces,
1119 interfaceOrder,
1120 defaultRoute,
1121 flags,
1122 errorMessage);
1123 _IsReachableFree(services, interfaces, interfaceOrder, defaultRoute);
1124
1125 (void) SCDClose(&session);
1126 return scn_status;
1127 }
1128
1129
1130 SCNStatus
1131 SCNIsReachableByName(const char *nodename,
1132 int *flags,
1133 const char **errorMessage)
1134 {
1135 struct in_addr *defaultRoute = NULL;
1136 struct hostent *h;
1137 int i;
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;
1147
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);
1153 }
1154 goto done;
1155 }
1156
1157 _IsReachableInit(session, &services, &interfaces, &interfaceOrder, &defaultRoute);
1158
1159 /*
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.
1163 */
1164 res_init();
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,
1169 services,
1170 interfaces,
1171 interfaceOrder,
1172 defaultRoute,
1173 flags,
1174 errorMessage);
1175 if (ns_status < scn_status) {
1176 /* return the worst case result */
1177 scn_status = ns_status;
1178 if (ns_status == SCN_REACHABLE_UNKNOWN) {
1179 /* not today */
1180 break;
1181 }
1182 }
1183 }
1184
1185 if (ns_status < SCN_REACHABLE_YES) {
1186 goto done;
1187 }
1188
1189 /*
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.
1194 */
1195 scn_status = SCN_REACHABLE_UNKNOWN;
1196
1197 /*
1198 * resolve the nodename into an address
1199 */
1200 i = getaddrinfo(nodename, NULL, NULL, &res);
1201 if (i != 0) {
1202 SCDSessionLog(session,
1203 LOG_ERR,
1204 CFSTR("getaddrinfo() failed: %s"),
1205 gai_strerror(i));
1206 goto done;
1207 }
1208
1209 for (resP=res; resP!=NULL; resP=resP->ai_next) {
1210 ns_status = checkAddress(session,
1211 resP->ai_addr,
1212 resP->ai_addrlen,
1213 services,
1214 interfaces,
1215 interfaceOrder,
1216 defaultRoute,
1217 flags,
1218 errorMessage);
1219 if (ns_status > scn_status) {
1220 /* return the best case result */
1221 scn_status = ns_status;
1222 if (ns_status == SCN_REACHABLE_YES) {
1223 /* we're in luck */
1224 break;
1225 }
1226 }
1227 }
1228
1229 if (res) {
1230 goto done;
1231 }
1232
1233 /*
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.
1240 */
1241
1242 #ifdef DEBUG
1243 if (SCDOptionGet(session, kSCDOptionDebug))
1244 SCDLog(LOG_INFO, CFSTR("getaddrinfo() returned no addresses, try gethostbyname()"));
1245 #endif /* DEBUG */
1246
1247 h = gethostbyname(nodename);
1248 if (h && h->h_length) {
1249 struct in_addr **s = (struct in_addr **)h->h_addr_list;
1250
1251 while (*s) {
1252 struct sockaddr_in sa;
1253
1254 bzero(&sa, sizeof(sa));
1255 sa.sin_len = sizeof(sa);
1256 sa.sin_family = AF_INET;
1257 sa.sin_addr = **s;
1258
1259 ns_status = checkAddress(session,
1260 (struct sockaddr *)&sa,
1261 sizeof(sa),
1262 services,
1263 interfaces,
1264 interfaceOrder,
1265 defaultRoute,
1266 flags,
1267 errorMessage);
1268 if (ns_status > scn_status) {
1269 /* return the best case result */
1270 scn_status = ns_status;
1271 if (ns_status == SCN_REACHABLE_YES) {
1272 /* we're in luck */
1273 break;
1274 }
1275 }
1276
1277 s++;
1278 }
1279 }
1280
1281 done :
1282
1283 _IsReachableFree(services, interfaces, interfaceOrder, defaultRoute);
1284 if (session) (void)SCDClose(&session);
1285 if (res) freeaddrinfo(res);
1286
1287 return scn_status;
1288 }