]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetwork.c
b413ec80add253193e440d6dbe099b36983d404f
[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 /*
24 * Modification History
25 *
26 * June 10, 2001 Allan Nathanson <ajn@apple.com>
27 * - updated to use service-based "State:" information
28 *
29 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
31 *
32 * January 30, 2001 Allan Nathanson <ajn@apple.com>
33 * - initial revision
34 */
35
36 #include <SystemConfiguration/SystemConfiguration.h>
37 #include <SystemConfiguration/SCPrivate.h>
38 #include <SystemConfiguration/SCValidation.h>
39
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <arpa/nameser.h>
43 #include <netdb.h>
44 #include <resolv.h>
45 #include <unistd.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48 #include <net/if.h>
49
50 #include "ppp.h"
51
52 static int
53 inet_atonCF(CFStringRef cfStr, struct in_addr *addr)
54 {
55 char cStr[sizeof("255.255.255.255")];
56
57 if (!CFStringGetCString(cfStr, cStr, sizeof(cStr), kCFStringEncodingMacRoman)) {
58 return 0;
59 }
60
61 return inet_aton(cStr, addr);
62 }
63
64
65 /*
66 * Function: parse_component
67 * Purpose:
68 * Given a string 'key' and a string prefix 'prefix',
69 * return the next component in the slash '/' separated
70 * key.
71 *
72 * Examples:
73 * 1. key = "a/b/c" prefix = "a/"
74 * returns "b"
75 * 2. key = "a/b/c" prefix = "a/b/"
76 * returns "c"
77 */
78 static CFStringRef
79 parse_component(CFStringRef key, CFStringRef prefix)
80 {
81 CFMutableStringRef comp;
82 CFRange range;
83
84 if (CFStringHasPrefix(key, prefix) == FALSE) {
85 return NULL;
86 }
87 comp = CFStringCreateMutableCopy(NULL, 0, key);
88 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
89 range = CFStringFind(comp, CFSTR("/"), 0);
90 if (range.location == kCFNotFound) {
91 return comp;
92 }
93 range.length = CFStringGetLength(comp) - range.location;
94 CFStringDelete(comp, range);
95 return comp;
96 }
97
98
99 typedef struct {
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;
108
109
110 static void
111 collectInfo(const void *key, const void *value, void *context)
112 {
113 initContextRef info = (initContextRef)context;
114 CFStringRef interface;
115 CFStringRef interfaceKey;
116 CFStringRef service;
117 CFStringRef serviceKey;
118
119 if (!isA_CFString(key) || !isA_CFDictionary(value)) {
120 return;
121 }
122
123 service = parse_component((CFStringRef)key, info->cPrefix);
124 if (service) {
125 serviceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
126 kSCDynamicStoreDomainSetup,
127 service,
128 kSCEntNetIPv4);
129 if (CFEqual((CFStringRef)key, serviceKey)) {
130 CFMutableDictionaryRef dict;
131
132 dict = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
133 CFDictionaryAddValue(info->cDict, service, dict);
134 CFRelease(dict);
135 }
136 CFRelease(serviceKey);
137
138 if (!CFArrayContainsValue(info->order, CFRangeMake(0, CFArrayGetCount(info->order)), service)) {
139 CFArrayAppendValue(info->order, service);
140 }
141
142 CFRelease(service);
143 return;
144 }
145
146 service = parse_component((CFStringRef)key, info->aPrefix);
147 if (service) {
148 serviceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
149 kSCDynamicStoreDomainState,
150 service,
151 kSCEntNetIPv4);
152 if (CFEqual((CFStringRef)key, serviceKey)) {
153 CFMutableDictionaryRef dict;
154
155 dict = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
156 CFDictionaryAddValue(info->aDict, service, dict);
157 CFRelease(dict);
158 }
159 CFRelease(serviceKey);
160
161 if (!CFArrayContainsValue(info->order, CFRangeMake(0, CFArrayGetCount(info->order)), service)) {
162 CFArrayAppendValue(info->order, service);
163 }
164
165 CFRelease(service);
166 return;
167 }
168
169 interface = parse_component((CFStringRef)key, info->iPrefix);
170 if (interface) {
171 interfaceKey = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
172 kSCDynamicStoreDomainState,
173 interface,
174 kSCEntNetIPv4);
175 if (CFEqual((CFStringRef)key, interfaceKey)) {
176 CFMutableDictionaryRef dict;
177
178 dict = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
179 CFDictionaryAddValue(info->iDict, interface, dict);
180 CFRelease(dict);
181 }
182 CFRelease(interfaceKey);
183 CFRelease(interface);
184 return;
185 }
186
187 return;
188 }
189
190
191 static void
192 collectExtraInfo(const void *key, const void *value, void *context)
193 {
194 CFStringRef interfaceKey;
195 initContextRef info = (initContextRef)context;
196 CFMutableDictionaryRef dict;
197 Boolean match;
198 CFStringRef pppKey;
199 CFStringRef service;
200
201 if (!isA_CFString(key) || !isA_CFDictionary(value)) {
202 return;
203 }
204
205 service = parse_component((CFStringRef)key, info->cPrefix);
206 if (!service) {
207 /* this key/value pair contains supplemental information */
208 return;
209 }
210
211 dict = (CFMutableDictionaryRef)CFDictionaryGetValue(info->cDict, service);
212 if (!dict) {
213 /* we don't have any IPv4 information for this service */
214 goto done;
215 }
216
217 interfaceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
218 kSCDynamicStoreDomainSetup,
219 service,
220 kSCEntNetInterface);
221 match = CFEqual((CFStringRef)key, interfaceKey);
222 CFRelease(interfaceKey);
223 if (match) {
224 CFStringRef interface;
225
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);
232 }
233 goto done;
234 }
235
236 pppKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
237 kSCDynamicStoreDomainSetup,
238 service,
239 kSCEntNetPPP);
240 match = CFEqual((CFStringRef)key, pppKey);
241 CFRelease(pppKey);
242 if (match) {
243 CFNumberRef dialOnDemand;
244
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);
251 }
252 goto done;
253 }
254
255 done :
256
257 CFRelease(service);
258 return;
259 }
260
261
262 static void
263 removeKnownAddresses(const void *key, const void *value, void *context)
264 {
265 CFMutableDictionaryRef ifDict;
266 CFStringRef ifName;
267 CFMutableDictionaryRef interfaces = (CFMutableDictionaryRef)context;
268 CFMutableDictionaryRef serviceDict = (CFMutableDictionaryRef)value;
269 Boolean updated = FALSE;
270
271 CFIndex i;
272 CFArrayRef iAddrs;
273 CFArrayRef iDests;
274 CFArrayRef iMasks;
275 CFIndex n;
276 CFMutableArrayRef nAddrs = NULL;
277 CFMutableArrayRef nDests = NULL;
278 CFMutableArrayRef nMasks = NULL;
279 CFIndex s;
280 CFArrayRef sAddrs;
281 CFArrayRef sDests;
282 CFArrayRef sMasks;
283
284 ifName = CFDictionaryGetValue(serviceDict, kSCPropInterfaceName);
285 if (!ifName) {
286 /* if no "InterfaceName" for this service */
287 return;
288 }
289
290 ifDict = (CFMutableDictionaryRef)CFDictionaryGetValue(interfaces, ifName);
291 if (!ifDict) {
292 /* if the indicated interface is not active */
293 return;
294 }
295
296 sAddrs = isA_CFArray(CFDictionaryGetValue(serviceDict,
297 kSCPropNetIPv4Addresses));
298 sDests = isA_CFArray(CFDictionaryGetValue(serviceDict,
299 kSCPropNetIPv4DestAddresses));
300 sMasks = isA_CFArray(CFDictionaryGetValue(serviceDict,
301 kSCPropNetIPv4SubnetMasks));
302
303 if (!sAddrs || ((n = CFArrayGetCount(sAddrs)) == 0)) {
304 /* if no addresses */
305 return;
306 }
307
308 if (((sMasks == NULL) && (sDests == NULL)) ||
309 ((sMasks != NULL) && (sDests != NULL))) {
310 /*
311 * sorry, we expect to have "SubnetMasks" or
312 * "DestAddresses" (not both).
313 */
314 return;
315 }
316
317 if (sMasks && (n != CFArrayGetCount(sMasks))) {
318 /* if we don't like the "SubnetMasks" */
319 return;
320 }
321
322 if (sDests && (n != CFArrayGetCount(sDests))) {
323 /* if we don't like the "DestAddresses" */
324 return;
325 }
326
327 iAddrs = isA_CFArray(CFDictionaryGetValue(ifDict,
328 kSCPropNetIPv4Addresses));
329 iDests = isA_CFArray(CFDictionaryGetValue(ifDict,
330 kSCPropNetIPv4DestAddresses));
331 iMasks = isA_CFArray(CFDictionaryGetValue(ifDict,
332 kSCPropNetIPv4SubnetMasks));
333
334 if (((iMasks == NULL) && (iDests == NULL)) ||
335 ((iMasks != NULL) && (iDests != NULL))) {
336 /*
337 * sorry, we expect to have "SubnetMasks" or
338 * "DestAddresses" (not both).
339 */
340 return;
341 }
342
343 if (!iAddrs || ((i = CFArrayGetCount(iAddrs)) == 0)) {
344 /* if no addresses */
345 return;
346 }
347
348 if (iMasks && (i != CFArrayGetCount(iMasks))) {
349 /* if we don't like the "SubnetMasks" */
350 return;
351 }
352
353 if (iDests && (i != CFArrayGetCount(iDests))) {
354 /* if we don't like the "DestAddresses" */
355 return;
356 }
357
358 if (((sMasks == NULL) && (iMasks != NULL)) ||
359 ((sDests == NULL) && (iDests != NULL))) {
360 /* if our addressing schemes are in conflict */
361 return;
362 }
363
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);
369 while (--i >= 0) {
370 if (sMasks &&
371 CFEqual(CFArrayGetValueAtIndex(sAddrs, s),
372 CFArrayGetValueAtIndex(nAddrs, i)) &&
373 CFEqual(CFArrayGetValueAtIndex(sMasks, s),
374 CFArrayGetValueAtIndex(nMasks, i))
375 ) {
376 /* we have a match */
377 CFArrayRemoveValueAtIndex(nAddrs, i);
378 CFArrayRemoveValueAtIndex(nMasks, i);
379 updated = TRUE;
380 } else if (sDests &&
381 CFEqual(CFArrayGetValueAtIndex(sAddrs, s),
382 CFArrayGetValueAtIndex(nAddrs, i)) &&
383 CFEqual(CFArrayGetValueAtIndex(sDests, s),
384 CFArrayGetValueAtIndex(nDests, i))
385 ) {
386 /* we have a match */
387 CFArrayRemoveValueAtIndex(nAddrs, i);
388 CFArrayRemoveValueAtIndex(nDests, i);
389 updated = TRUE;
390 }
391 }
392 }
393
394 if (updated) {
395 if (nAddrs) {
396 CFDictionarySetValue(ifDict,
397 kSCPropNetIPv4Addresses,
398 nAddrs);
399 }
400 if (nMasks) {
401 CFDictionarySetValue(ifDict,
402 kSCPropNetIPv4SubnetMasks,
403 nMasks);
404 } else {
405 CFDictionarySetValue(ifDict,
406 kSCPropNetIPv4DestAddresses,
407 nDests);
408 }
409 CFDictionarySetValue(interfaces, ifName, ifDict);
410 }
411 CFRelease(nAddrs);
412 if (nMasks) CFRelease(nMasks);
413 if (nDests) CFRelease(nDests);
414
415 return;
416 }
417
418
419 static void
420 addUnknownService(const void *key, const void *value, void *context)
421 {
422 CFArrayRef addrs;
423 CFMutableDictionaryRef ifDict = (CFMutableDictionaryRef)value;
424 initContextRef info = (initContextRef)context;
425 CFStringRef service;
426 CFUUIDRef uuid;
427
428 addrs = CFDictionaryGetValue(ifDict, kSCPropNetIPv4Addresses);
429 if (!addrs || (CFArrayGetCount(addrs) == 0)) {
430 /* if no addresses */
431 return;
432 }
433
434 /* add the "InterfaceName" to the (new/fake) service dictionary */
435 CFDictionaryAddValue(ifDict, kSCPropInterfaceName, (CFStringRef)key);
436
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);
442 CFRelease(service);
443 CFRelease(uuid);
444
445 return;
446 }
447
448
449 static Boolean
450 getAddresses(CFDictionaryRef iDict,
451 CFIndex *nAddrs,
452 CFArrayRef *addrs,
453 CFArrayRef *masks,
454 CFArrayRef *dests)
455 {
456 *addrs = isA_CFArray(CFDictionaryGetValue(iDict,
457 kSCPropNetIPv4Addresses));
458 *masks = isA_CFArray(CFDictionaryGetValue(iDict,
459 kSCPropNetIPv4SubnetMasks));
460 *dests = isA_CFArray(CFDictionaryGetValue(iDict,
461 kSCPropNetIPv4DestAddresses));
462
463 if ((*addrs == NULL) ||
464 ((*nAddrs = CFArrayGetCount(*addrs)) == 0)) {
465 /* sorry, no addresses */
466 _SCErrorSet(kSCStatusReachabilityUnknown);
467 return FALSE;
468 }
469
470 if (((*masks == NULL) && (*dests == NULL)) ||
471 ((*masks != NULL) && (*dests != NULL))) {
472 /*
473 * sorry, we expect to have "SubnetMasks" or
474 * "DestAddresses" (not both) and the count
475 * must match the number of "Addresses".
476 */
477 _SCErrorSet(kSCStatusReachabilityUnknown);
478 return FALSE;
479 }
480
481 if (*masks && (*nAddrs != CFArrayGetCount(*masks))) {
482 /* if we don't like the netmasks */
483 _SCErrorSet(kSCStatusReachabilityUnknown);
484 return FALSE;
485 }
486
487 if (*dests && (*nAddrs != CFArrayGetCount(*dests))) {
488 /* if we don't like the destaddresses */
489 _SCErrorSet(kSCStatusReachabilityUnknown);
490 return FALSE;
491 }
492
493 return TRUE;
494 }
495
496 static Boolean
497 checkAddress(SCDynamicStoreRef store,
498 const struct sockaddr *address,
499 const int addrlen,
500 CFDictionaryRef config,
501 CFDictionaryRef active,
502 CFArrayRef serviceOrder,
503 struct in_addr *defaultRoute,
504 SCNetworkConnectionFlags *flags)
505 {
506 CFIndex aCnt;
507 CFStringRef aType = NULL;
508 CFDictionaryRef cDict = NULL;
509 CFIndex i;
510 CFStringRef key = NULL;
511 int pppRef = -1;
512 int sc_status = kSCStatusReachabilityUnknown;
513 char *statusMessage = NULL;
514
515 if (!address || !flags) {
516 sc_status = kSCStatusInvalidArgument;
517 goto done;
518 }
519
520 *flags = 0;
521
522 if (address->sa_family == AF_INET) {
523 struct sockaddr_in *sin = (struct sockaddr_in *)address;
524
525 SCLog(_sc_debug, LOG_INFO, CFSTR("checkAddress(%s)"), inet_ntoa(sin->sin_addr));
526
527 /*
528 * Check if the address is on one of the subnets
529 * associated with our active IPv4 interfaces
530 */
531 aCnt = CFArrayGetCount(serviceOrder);
532 for (i=0; i<aCnt; i++) {
533 CFDictionaryRef aDict;
534 CFArrayRef addrs;
535 CFArrayRef dests;
536 CFIndex j;
537 CFArrayRef masks;
538 CFIndex nAddrs = 0;
539
540 key = CFArrayGetValueAtIndex(serviceOrder, i);
541 aDict = CFDictionaryGetValue(active, key);
542
543 if (!aDict ||
544 !getAddresses(aDict, &nAddrs, &addrs, &masks, &dests)) {
545 /* if no addresses to check */
546 continue;
547 }
548
549 for (j=0; j<nAddrs; j++) {
550 struct in_addr ifAddr;
551
552 if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
553 &ifAddr) == 0) {
554 /* if Addresses string is invalid */
555 break;
556 }
557
558 if (masks) {
559 struct in_addr ifMask;
560
561 if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
562 &ifMask) == 0) {
563 /* if SubnetMask string is invalid */
564 break;
565 }
566
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;
572 goto checkInterface;
573 }
574 } else {
575 struct in_addr destAddr;
576
577 /* check remote address */
578 if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
579 &destAddr) == 0) {
580 /* if DestAddresses string is invalid */
581 break;
582 }
583
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;
589 goto checkInterface;
590 }
591
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;
596 goto checkInterface;
597 }
598 }
599 }
600 }
601
602 /*
603 * Check if the address is accessible via the "default" route.
604 */
605 for (i=0; i<aCnt; i++) {
606 CFDictionaryRef aDict;
607 CFArrayRef addrs;
608 CFArrayRef dests;
609 CFIndex j;
610 CFArrayRef masks;
611 CFIndex nAddrs = 0;
612
613 key = CFArrayGetValueAtIndex(serviceOrder, i);
614 aDict = CFDictionaryGetValue(active, key);
615
616 if (!sin->sin_addr.s_addr ||
617 !defaultRoute ||
618 !aDict ||
619 !getAddresses(aDict, &nAddrs, &addrs, &masks, &dests)) {
620 /* if no addresses to check */
621 continue;
622 }
623
624 for (j=0; defaultRoute && j<nAddrs; j++) {
625 if (masks) {
626 struct in_addr ifAddr;
627 struct in_addr ifMask;
628
629 if (inet_atonCF(CFArrayGetValueAtIndex(addrs, j),
630 &ifAddr) == 0) {
631 /* if Addresses string is invalid */
632 break;
633 }
634
635 if (inet_atonCF(CFArrayGetValueAtIndex(masks, j),
636 &ifMask) == 0) {
637 /* if SubnetMasks string is invalid */
638 break;
639 }
640
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;
646 goto checkInterface;
647 }
648 } else {
649 struct in_addr destAddr;
650
651 /* check remote address */
652 if (inet_atonCF(CFArrayGetValueAtIndex(dests, j),
653 &destAddr) == 0) {
654 /* if DestAddresses string is invalid */
655 break;
656 }
657
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;
662 goto checkInterface;
663 }
664 }
665 }
666 }
667
668 /*
669 * Check the not active (but configured) IPv4 services
670 */
671 for (i=0; i<aCnt; i++) {
672 key = CFArrayGetValueAtIndex(serviceOrder, i);
673
674 if (CFDictionaryContainsKey(active, key)) {
675 /* if this service is active */
676 continue;
677 }
678
679 cDict = CFDictionaryGetValue(config, key);
680 if (!cDict) {
681 /* if no configuration for this service */
682 continue;
683 }
684
685 /*
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.
689 */
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;
694 goto done;
695 }
696
697 statusMessage = "is configured w/dynamic addressing";
698 *flags |= kSCNetworkFlagsTransientConnection;
699 *flags |= kSCNetworkFlagsReachable;
700 *flags |= kSCNetworkFlagsConnectionRequired;
701
702 if (_sc_debug) {
703 SCLog(TRUE, LOG_INFO, CFSTR(" status = %s"), statusMessage);
704 SCLog(TRUE, LOG_INFO, CFSTR(" service id = %@"), key);
705 }
706
707 sc_status = kSCStatusOK;
708 goto done;
709 }
710
711 SCLog(_sc_debug, LOG_INFO, CFSTR(" cannot be reached"));
712 sc_status = kSCStatusOK;
713 goto done;
714
715 } else {
716 /*
717 * if no code for this address family (yet)
718 */
719 SCLog(_sc_verbose, LOG_ERR,
720 CFSTR("checkAddress(): unexpected address family %d"),
721 address->sa_family);
722 sc_status = kSCStatusInvalidArgument;
723 goto done;
724 }
725
726 goto done;
727
728 checkInterface :
729
730 if (_sc_debug) {
731 CFDictionaryRef aDict;
732 CFStringRef interface = NULL;
733
734 /* attempt to get the interface type from the config info */
735 aDict = CFDictionaryGetValue(active, key);
736 if (aDict) {
737 interface = CFDictionaryGetValue(aDict, kSCPropInterfaceName);
738 }
739
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("?"));
743 }
744
745 sc_status = kSCStatusOK;
746
747 /*
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.
751 */
752 {
753 CFNumberRef num;
754 CFDictionaryRef cDict;
755
756 /* attempt to get the interface type from the config info */
757 cDict = CFDictionaryGetValue(config, key);
758 if (cDict) {
759 aType = CFDictionaryGetValue(cDict, kSCPropNetInterfaceType);
760 }
761
762 if (!aType || !CFEqual(aType, kSCValNetInterfaceTypePPP)) {
763 /*
764 * if we don't know the interface type or if
765 * it is not a ppp interface
766 */
767 goto done;
768 }
769
770 num = CFDictionaryGetValue(cDict, kSCPropNetPPPDialOnDemand);
771 if (num) {
772 int dialOnDemand;
773
774 CFNumberGetValue(num, kCFNumberIntType, &dialOnDemand);
775 if (dialOnDemand != 0) {
776 *flags |= kSCNetworkFlagsConnectionAutomatic;
777 }
778
779 }
780 }
781
782 *flags |= kSCNetworkFlagsTransientConnection;
783
784 {
785 u_int32_t pppLink;
786 struct ppp_status *pppLinkStatus;
787 int pppStatus;
788
789 /*
790 * The service ID is available, ask the PPP controller
791 * for the extended status.
792 */
793 pppStatus = PPPInit(&pppRef);
794 if (pppStatus != 0) {
795 SCLog(_sc_debug, LOG_DEBUG, CFSTR(" PPPInit() failed: status=%d"), pppStatus);
796 sc_status = kSCStatusReachabilityUnknown;
797 goto done;
798 }
799
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;
804 goto done;
805 }
806
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;
811 goto done;
812 }
813 #ifdef DEBUG
814 SCLog(_sc_debug, LOG_DEBUG, CFSTR(" PPP link status = %d"), pppLinkStatus->status);
815 #endif /* DEBUG */
816 switch (pppLinkStatus->status) {
817 case PPP_RUNNING :
818 /* if we're really UP and RUNNING */
819 break;
820 case PPP_IDLE :
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;
826 break;
827 default :
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;
833 break;
834 }
835 CFAllocatorDeallocate(NULL, pppLinkStatus);
836 }
837
838 goto done;
839
840 done :
841
842 if (pppRef != -1) (void) PPPDispose(pppRef);
843
844 if (sc_status != kSCStatusOK) {
845 _SCErrorSet(sc_status);
846 return FALSE;
847 }
848
849 return TRUE;
850 }
851
852
853 static void
854 _CheckReachabilityInit(SCDynamicStoreRef store,
855 CFDictionaryRef *config,
856 CFDictionaryRef *active,
857 CFArrayRef *serviceOrder,
858 struct in_addr **defaultRoute)
859 {
860 CFMutableDictionaryRef activeDict;
861 CFMutableDictionaryRef configDict;
862 initContext context;
863 CFDictionaryRef dict;
864 CFMutableDictionaryRef interfaces;
865 CFMutableArrayRef keys;
866 CFMutableArrayRef orderArray;
867 CFDictionaryRef orderDict;
868 CFStringRef orderKey;
869 CFStringRef pattern;
870 CFMutableArrayRef patterns;
871 CFStringRef routeKey;
872 CFDictionaryRef routeDict;
873
874 configDict = CFDictionaryCreateMutable(NULL,
875 0,
876 &kCFTypeDictionaryKeyCallBacks,
877 &kCFTypeDictionaryValueCallBacks);
878 *config = configDict;
879
880 activeDict = CFDictionaryCreateMutable(NULL,
881 0,
882 &kCFTypeDictionaryKeyCallBacks,
883 &kCFTypeDictionaryValueCallBacks);
884 *active = activeDict;
885
886 orderArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
887 *serviceOrder = orderArray;
888
889 *defaultRoute = NULL;
890
891 interfaces = CFDictionaryCreateMutable(NULL,
892 0,
893 &kCFTypeDictionaryKeyCallBacks,
894 &kCFTypeDictionaryValueCallBacks);
895
896 /*
897 * collect information on the configured services and their
898 * associated interface type.
899 */
900 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
901 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
902
903 /*
904 * Setup:/Network/Global/IPv4 (for the ServiceOrder)
905 */
906 orderKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
907 kSCDynamicStoreDomainSetup,
908 kSCEntNetIPv4);
909 CFArrayAppendValue(keys, orderKey);
910
911 /*
912 * State:/Network/Global/IPv4 (for the DefaultRoute)
913 */
914 routeKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
915 kSCDynamicStoreDomainState,
916 kSCEntNetIPv4);
917 CFArrayAppendValue(keys, routeKey);
918
919 /* Setup: per-service IPv4 info */
920 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
921 kSCDynamicStoreDomainSetup,
922 kSCCompAnyRegex,
923 kSCEntNetIPv4);
924 CFArrayAppendValue(patterns, pattern);
925 CFRelease(pattern);
926
927 /* Setup: per-service Interface info */
928 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
929 kSCDynamicStoreDomainSetup,
930 kSCCompAnyRegex,
931 kSCEntNetInterface);
932 CFArrayAppendValue(patterns, pattern);
933 CFRelease(pattern);
934
935 /* Setup: per-service PPP info */
936 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
937 kSCDynamicStoreDomainSetup,
938 kSCCompAnyRegex,
939 kSCEntNetPPP);
940 CFArrayAppendValue(patterns, pattern);
941 CFRelease(pattern);
942
943 /* State: per-service IPv4 info */
944 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
945 kSCDynamicStoreDomainState,
946 kSCCompAnyRegex,
947 kSCEntNetIPv4);
948 CFArrayAppendValue(patterns, pattern);
949 CFRelease(pattern);
950
951 /* State: per-interface IPv4 info */
952 pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
953 kSCDynamicStoreDomainState,
954 kSCCompAnyRegex,
955 kSCEntNetIPv4);
956 CFArrayAppendValue(patterns, pattern);
957 CFRelease(pattern);
958
959 /* fetch the configuration information */
960 dict = SCDynamicStoreCopyMultiple(store, keys, patterns);
961 CFRelease(keys);
962 CFRelease(patterns);
963 if (!dict) {
964 goto done;
965 }
966
967 /*
968 * get the ServiceOrder key from the global settings.
969 */
970 orderDict = CFDictionaryGetValue(dict, orderKey);
971 if (isA_CFDictionary(orderDict)) {
972 CFArrayRef array;
973
974 /* global settings are available */
975 array = (CFMutableArrayRef)CFDictionaryGetValue(orderDict, kSCPropNetServiceOrder);
976 if (isA_CFArray(array)) {
977 CFArrayAppendArray(orderArray,
978 array,
979 CFRangeMake(0, CFArrayGetCount(array)));
980 }
981 }
982
983 /*
984 * get the DefaultRoute
985 */
986 routeDict = CFDictionaryGetValue(dict, routeKey);
987 if (isA_CFDictionary(routeDict)) {
988 CFStringRef addr;
989
990 /* global state is available, get default route */
991 addr = CFDictionaryGetValue(routeDict, kSCPropNetIPv4Router);
992 if (isA_CFString(addr)) {
993 struct in_addr *route;
994
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);
999 route = NULL;
1000 } else {
1001 *defaultRoute = route;
1002 }
1003 }
1004 }
1005
1006 /*
1007 * collect the configured services, the active services, and
1008 * the active interfaces.
1009 */
1010 context.cDict = configDict;
1011 context.cPrefix = SCDynamicStoreKeyCreate(NULL,
1012 CFSTR("%@/%@/%@/"),
1013 kSCDynamicStoreDomainSetup,
1014 kSCCompNetwork,
1015 kSCCompService);
1016 context.aDict = activeDict;
1017 context.aPrefix = SCDynamicStoreKeyCreate(NULL,
1018 CFSTR("%@/%@/%@/"),
1019 kSCDynamicStoreDomainState,
1020 kSCCompNetwork,
1021 kSCCompService);
1022 context.iDict = interfaces;
1023 context.iPrefix = SCDynamicStoreKeyCreate(NULL,
1024 CFSTR("%@/%@/%@/"),
1025 kSCDynamicStoreDomainState,
1026 kSCCompNetwork,
1027 kSCCompInterface);
1028 context.order = orderArray;
1029
1030 CFDictionaryApplyFunction(dict, collectInfo, &context);
1031
1032 /*
1033 * add additional information for the configured services
1034 */
1035 CFDictionaryApplyFunction(dict, collectExtraInfo, &context);
1036
1037 /*
1038 * remove any addresses associated with known services
1039 */
1040 CFDictionaryApplyFunction(activeDict, removeKnownAddresses, interfaces);
1041
1042 /*
1043 * create new services for any remaining addresses
1044 */
1045 CFDictionaryApplyFunction(interfaces, addUnknownService, &context);
1046
1047 CFRelease(context.cPrefix);
1048 CFRelease(context.aPrefix);
1049 CFRelease(context.iPrefix);
1050 CFRelease(dict);
1051
1052 done :
1053
1054 CFRelease(interfaces);
1055 CFRelease(orderKey);
1056 CFRelease(routeKey);
1057
1058 #ifdef DEBUG
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");
1063 #endif /* DEBUG */
1064 return;
1065 }
1066
1067
1068 static void
1069 _CheckReachabilityFree(CFDictionaryRef config,
1070 CFDictionaryRef active,
1071 CFArrayRef serviceOrder,
1072 struct in_addr *defaultRoute)
1073 {
1074 if (config) CFRelease(config);
1075 if (active) CFRelease(active);
1076 if (serviceOrder) CFRelease(serviceOrder);
1077 if (defaultRoute) CFAllocatorDeallocate(NULL, defaultRoute);
1078 return;
1079 }
1080
1081
1082 Boolean
1083 SCNetworkCheckReachabilityByAddress(const struct sockaddr *address,
1084 const int addrlen,
1085 SCNetworkConnectionFlags *flags)
1086 {
1087 CFDictionaryRef active = NULL;
1088 CFDictionaryRef config = NULL;
1089 struct in_addr *defaultRoute = NULL;
1090 Boolean ok;
1091 CFArrayRef serviceOrder = NULL;
1092 SCDynamicStoreRef store = NULL;
1093
1094 *flags = 0;
1095
1096 /*
1097 * Check if 0.0.0.0
1098 */
1099 if (address->sa_family == AF_INET) {
1100 struct sockaddr_in *sin = (struct sockaddr_in *)address;
1101
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;
1106 return TRUE;
1107 }
1108 }
1109
1110 store = SCDynamicStoreCreate(NULL,
1111 CFSTR("SCNetworkCheckReachabilityByAddress"),
1112 NULL,
1113 NULL);
1114 if (!store) {
1115 SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
1116 return FALSE;
1117 }
1118
1119 _CheckReachabilityInit(store, &config, &active, &serviceOrder, &defaultRoute);
1120 ok = checkAddress(store,
1121 address,
1122 addrlen,
1123 config,
1124 active,
1125 serviceOrder,
1126 defaultRoute,
1127 flags);
1128 _CheckReachabilityFree(config, active, serviceOrder, defaultRoute);
1129
1130 CFRelease(store);
1131 return ok;
1132 }
1133
1134
1135 /*
1136 * rankReachability()
1137 * Not reachable == 0
1138 * Connection Required == 1
1139 * Reachable == 2
1140 */
1141 static int
1142 rankReachability(int flags)
1143 {
1144 int rank = 0;
1145
1146 if (flags & kSCNetworkFlagsReachable) rank = 2;
1147 if (flags & kSCNetworkFlagsConnectionRequired) rank = 1;
1148 return rank;
1149 }
1150
1151
1152 Boolean
1153 SCNetworkCheckReachabilityByName(const char *nodename,
1154 SCNetworkConnectionFlags *flags)
1155 {
1156 CFDictionaryRef active = NULL;
1157 CFDictionaryRef config = NULL;
1158 struct in_addr *defaultRoute = NULL;
1159 struct hostent *h;
1160 Boolean haveDNS = FALSE;
1161 int i;
1162 Boolean ok = TRUE;
1163 struct addrinfo *res = NULL;
1164 struct addrinfo *resP;
1165 CFArrayRef serviceOrder = NULL;
1166 SCDynamicStoreRef store = NULL;
1167
1168 store = SCDynamicStoreCreate(NULL,
1169 CFSTR("SCNetworkCheckReachabilityByName"),
1170 NULL,
1171 NULL);
1172 if (!store) {
1173 SCLog(_sc_verbose, LOG_INFO, CFSTR("SCDynamicStoreCreate() failed"));
1174 return FALSE;
1175 }
1176
1177 _CheckReachabilityInit(store, &config, &active, &serviceOrder, &defaultRoute);
1178
1179 /*
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.
1185 */
1186
1187 *flags = kSCNetworkFlagsReachable;
1188
1189 res_init();
1190 for (i=0; i<_res.nscount; i++) {
1191 SCNetworkConnectionFlags ns_flags = 0;
1192
1193 if (_res.nsaddr_list[i].sin_addr.s_addr == 0) {
1194 continue;
1195 }
1196
1197 haveDNS = TRUE;
1198
1199 if (_res.nsaddr_list[i].sin_len == 0) {
1200 _res.nsaddr_list[i].sin_len = sizeof(_res.nsaddr_list[i]);
1201 }
1202
1203 ok = checkAddress(store,
1204 (struct sockaddr *)&_res.nsaddr_list[i],
1205 _res.nsaddr_list[i].sin_len,
1206 config,
1207 active,
1208 serviceOrder,
1209 defaultRoute,
1210 &ns_flags);
1211 if (!ok) {
1212 /* not today */
1213 break;
1214 }
1215 if (rankReachability(ns_flags) < rankReachability(*flags)) {
1216 /* return the worst case result */
1217 *flags = ns_flags;
1218 }
1219 }
1220
1221 if (!ok || (rankReachability(*flags) < 2)) {
1222 goto done;
1223 }
1224
1225 SCLog(_sc_debug, LOG_INFO, CFSTR("check DNS for \"%s\""), nodename);
1226
1227 /*
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.
1232 */
1233 *flags = 0;
1234
1235 /*
1236 * resolve the nodename into an address
1237 */
1238 i = getaddrinfo(nodename, NULL, NULL, &res);
1239 if (i != 0) {
1240 SCLog(_sc_verbose, LOG_ERR,
1241 CFSTR("getaddrinfo() failed: %s"),
1242 gai_strerror(i));
1243 goto done;
1244 }
1245
1246 for (resP=res; resP!=NULL; resP=resP->ai_next) {
1247 SCNetworkConnectionFlags ns_flags = 0;
1248
1249 if (resP->ai_addr->sa_family == AF_INET) {
1250 struct sockaddr_in *sin = (struct sockaddr_in *)resP->ai_addr;
1251
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;
1256 break;
1257 }
1258 }
1259
1260 ok = checkAddress(store,
1261 resP->ai_addr,
1262 resP->ai_addrlen,
1263 config,
1264 active,
1265 serviceOrder,
1266 defaultRoute,
1267 &ns_flags);
1268 if (!ok) {
1269 /* not today */
1270 break;
1271 }
1272 if (rankReachability(ns_flags) > rankReachability(*flags)) {
1273 /* return the best case result */
1274 *flags = ns_flags;
1275 if (rankReachability(*flags) == 2) {
1276 /* we're in luck */
1277 break;
1278 }
1279 }
1280 }
1281
1282 if (res) {
1283 goto done;
1284 }
1285
1286 /*
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.
1293 */
1294
1295 #ifdef DEBUG
1296 SCLog(_sc_debug,
1297 LOG_INFO,
1298 CFSTR("getaddrinfo() returned no addresses, try gethostbyname()"));
1299 #endif /* DEBUG */
1300
1301 h = gethostbyname(nodename);
1302 if (h && h->h_length) {
1303 struct in_addr **s = (struct in_addr **)h->h_addr_list;
1304
1305 while (*s) {
1306 SCNetworkConnectionFlags ns_flags = 0;
1307 struct sockaddr_in sa;
1308
1309 bzero(&sa, sizeof(sa));
1310 sa.sin_len = sizeof(sa);
1311 sa.sin_family = AF_INET;
1312 sa.sin_addr = **s;
1313
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;
1318 break;
1319 }
1320
1321 ok = checkAddress(store,
1322 (struct sockaddr *)&sa,
1323 sizeof(sa),
1324 config,
1325 active,
1326 serviceOrder,
1327 defaultRoute,
1328 &ns_flags);
1329 if (!ok) {
1330 /* not today */
1331 break;
1332 }
1333 if (rankReachability(ns_flags) > rankReachability(*flags)) {
1334 /* return the best case result */
1335 *flags = ns_flags;
1336 if (rankReachability(*flags) == 2) {
1337 /* we're in luck */
1338 break;
1339 }
1340 }
1341
1342 s++;
1343 }
1344 } else {
1345 char *msg;
1346
1347 switch(h_errno) {
1348 case NETDB_INTERNAL :
1349 msg = strerror(errno);
1350 break;
1351 case HOST_NOT_FOUND :
1352 msg = "Host not found.";
1353 if (!haveDNS) {
1354 /*
1355 * No DNS servers are defined. Set flags based on
1356 * the availability of configured (but not active)
1357 * services.
1358 */
1359 struct sockaddr_in sa;
1360
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,
1367 sizeof(sa),
1368 config,
1369 active,
1370 serviceOrder,
1371 defaultRoute,
1372 flags);
1373 if (ok &&
1374 (*flags & kSCNetworkFlagsReachable) &&
1375 (*flags & kSCNetworkFlagsConnectionRequired)) {
1376 /*
1377 * We might pick up a set of DNS servers
1378 * from this connection, don't reply with
1379 * "Host not found." just yet.
1380 */
1381 goto done;
1382 }
1383 *flags = 0;
1384 }
1385 break;
1386 case TRY_AGAIN :
1387 msg = "Try again.";
1388 break;
1389 case NO_RECOVERY :
1390 msg = "No recovery.";
1391 break;
1392 case NO_DATA :
1393 msg = "No data available.";
1394 break;
1395 default :
1396 msg = "Unknown";
1397 break;
1398 }
1399 SCLog(_sc_debug, LOG_INFO, CFSTR("gethostbyname() failed: %s"), msg);
1400 }
1401
1402 done :
1403
1404 _CheckReachabilityFree(config, active, serviceOrder, defaultRoute);
1405 if (store) CFRelease(store);
1406 if (res) freeaddrinfo(res);
1407
1408 return ok;
1409 }