DNS 解析器(DNS Resolver)
DNS 解析器模块可保护用户免受 DNS 拦截和配置更新攻击,并改进了 DNS 解析的网络性能。此模块包含用于实现 DNS 桩解析器的代码,该解析器可将 www.google.com 等名称转换为 IP 地址(例如 2001:db8::1)。DNS 桩解析器支持 Java API 元素(如 InetAddress#getAllByName 和 Network#getAllByName)以及原生网络功能,且可发送和接收 DNS 查询以及缓存结果。
Android 10 中的变化
在搭载 Android 9 及更低版本的设备上,DNS 解析器代码分布在 Bionic 和 netd
上。DNS 查找操作集中在 netd
守护程序中,以便进行系统级缓存,而应用在 Bionic 中调用函数(例如 getaddrinfo
)。查询会通过 UNIX 套接字发送到 /dev/socket/dnsproxyd
,再到 netd
守护程序,该守护程序会解析请求并再次调用 getaddrinfo
,以发出 DNS 查找请求,然后它会缓存结果以供其他应用使用。DNS 解析器实现主要包含在 bionic/libc/dns/
中,部分包含在 system/netd/server/dns
中。
Android 10 将 DNS 解析器代码移至 system/netd/resolv,
,将其转换为 C++,然后对代码进行了翻新和重构。由于应用兼容性方面的原因,Bionic 中的代码继续存在,但系统不会再调用它们。以下源文件路径受到重构的影响:
bionic/libc/dns
system/netd/client
system/netd/server/dns
system/netd/server/DnsProxyListener.*
system/netd/resolv
(1)获取ip地址(域名–>IP地址)
ConnectivityService.updateDnses() ->
DnsManager.setDnsConfigurationForNetwork() ->
mDnsResolver.setResolverConfiguration() -> IDnsResolver.aidl ()
DnsResolverService.setResolverConfiguration() ->
ResolverController.setResolverConfiguration() ->
res_cache.int resolv_set_nameservers_for_net() ->
getaddrinfo.getaddrinfo_numeric() -> getaddrinfo.android_getaddrinfofornetcontext(hostname, servname, &hints, &netcontext, result);
(2)DNS server list 设置(set name servers for a network)
_resolv_set_nameservers_for_net
http://androidxref.com/9.0.0_r3/xref/libcore/ojluni/src/main/java/java/net/InetAddress.java#1105
-
1077 /**
-
1078 * Determines the IP address of a host, given the host's name.
-
1097 * @param host the specified host, or {@code null}.
-
1098 * @return an IP address for the given host name.
-
1104 */
-
1105 public static InetAddress getByName(String host)
-
1106 throws UnknownHostException {
-
1107 // Android-changed: Rewritten on the top of Libcore.os.
-
1108 return impl.lookupAllHostAddr(host, NETID_UNSET)[0];
-
1109 }
-
1111 /**
-
1112 * Given the name of a host, returns an array of its IP addresses,
-
1113 * based on the configured name service on the system.
-
1139 * @param host the name of the host, or {@code null}.
-
1140 * @return an array of all the IP addresses for a given host name.
-
1149 */
-
1150 public static InetAddress[] getAllByName(String host)
-
1151 throws UnknownHostException {
-
1152 // Android-changed: Resolves a hostname using Libcore.os.
-
1153 // Also, returns both the Inet4 and Inet6 loopback for null/empty host
-
1154 return impl.lookupAllHostAddr(host, NETID_UNSET).clone();
-
1155 }
lookupAllHostAddr在Android M上的名字叫做getAllByNameImpl
http://androidxref.com/9.0.0_r3/xref/libcore/ojluni/src/main/java/java/net/Inet6AddressImpl.java#88
-
79 // BEGIN Android-changed: Rewrote hostname lookup methods on top of Libcore.os.
-
80 /*
-
81 public native String getLocalHostName() throws UnknownHostException;
-
82 public native InetAddress[]
-
83 lookupAllHostAddr(String hostname) throws UnknownHostException;
-
84 public native String getHostByAddr(byte[] addr) throws UnknownHostException;
-
85 private native boolean isReachable0(byte[] addr, int scope, int timeout, byte[] inf, int ttl, int if_scope) throws IOException;
-
86 */
-
87 @Override
-
88 public InetAddress[] lookupAllHostAddr(String host, int netId) throws UnknownHostException {
-
89 if (host == null || host.isEmpty()) {
-
90 // Android-changed: Return both the Inet4 and Inet6 loopback addresses
-
91 // when host == null or empty.
-
92 return loopbackAddresses();
-
93 }
-
94
-
95 // Is it a numeric address?
-
96 InetAddress result = InetAddress.parseNumericAddressNoThrow(host);
-
97 if (result != null) {
-
98 result = InetAddress.disallowDeprecatedFormats(host, result);
-
99 if (result == null) {
-
100 throw new UnknownHostException("Deprecated IPv4 address format: " + host);
-
101 }
-
102 return new InetAddress[] { result };
-
103 }
-
104
-
105 return lookupHostByName(host, netId);
-
106 }
-
323 // BEGIN Android-changed: Let loopbackAddresses() return both Inet4 and Inet6 loopbacks.
-
324 @Override
-
325 public InetAddress[] loopbackAddresses() {
-
326 synchronized (Inet6AddressImpl.class) {
-
327 // We avoid initializing anyLocalAddress during <clinit> to avoid issues
-
328 // caused by the dependency chains of these classes. InetAddress depends on
-
329 // InetAddressImpl, but Inet6Address & Inet4Address are its subclasses.
-
330 // Also see {@code anyLocalAddress).
-
331 if (loopbackAddresses == null) {
-
332 loopbackAddresses = new InetAddress[]{Inet6Address.LOOPBACK, Inet4Address.LOOPBACK};
-
333 }
-
334
-
335 return loopbackAddresses;
-
336 }
-
337 }
http://androidxref.com/9.0.0_r3/xref/libcore/ojluni/src/main/java/java/net/InetAddress.java#1622
-
1622 static InetAddress parseNumericAddressNoThrow(String address) {
-
1623 // Accept IPv6 addresses (only) in square brackets for compatibility.
-
1624 if (address.startsWith("[") && address.endsWith("]") && address.indexOf(':') != -1) {
-
1625 address = address.substring(1, address.length() - 1);
-
1626 }
-
1627 StructAddrinfo hints = new StructAddrinfo();
-
1628 hints.ai_flags = AI_NUMERICHOST;
-
1629 InetAddress[] addresses = null;
-
1630 try {
-
1631 addresses = Libcore.os.android_getaddrinfo(address, hints, NETID_UNSET);
-
1632 } catch (GaiException ignored) {
-
1633 }
-
1634 return (addresses != null) ? addresses[0] : null;
-
1635 }
http://androidxref.com/9.0.0_r3/xref/libcore/ojluni/src/main/java/java/net/Inet6AddressImpl.java#115
-
108 /**
-
109 * Resolves a hostname to its IP addresses using a cache.
-
110 *
-
111 * @param host the hostname to resolve.
-
112 * @param netId the network to perform resolution upon.
-
113 * @return the IP addresses of the host.
-
114 */
-
115 private static InetAddress[] lookupHostByName(String host, int netId)
-
116 throws UnknownHostException {
-
117 BlockGuard.getThreadPolicy().onNetwork();
-
118 // Do we have a result cached?
-
119 Object cachedResult = addressCache.get(host, netId);
-
120 if (cachedResult != null) {
-
121 if (cachedResult instanceof InetAddress[]) {
-
122 // A cached positive result.
-
123 return (InetAddress[]) cachedResult;
-
124 } else {
-
125 // A cached negative result.
-
126 throw new UnknownHostException((String) cachedResult);
-
127 }
-
128 }
-
129 try {
-
130 StructAddrinfo hints = new StructAddrinfo();
-
131 hints.ai_flags = AI_ADDRCONFIG;
-
132 hints.ai_family = AF_UNSPEC;
-
133 // If we don't specify a socket type, every address will appear twice, once
-
134 // for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family
-
135 // anyway, just pick one.
-
136 hints.ai_socktype = SOCK_STREAM;
-
137 InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId);
-
138 // TODO: should getaddrinfo set the hostname of the InetAddresses it returns?
-
139 for (InetAddress address : addresses) {
-
140 address.holder().hostName = host;
-
141 address.holder().originalHostName = host;
-
142 }
-
143 addressCache.put(host, netId, addresses);
-
144 return addresses;
-
145 } catch (GaiException gaiException) {
-
146 // If the failure appears to have been a lack of INTERNET permission, throw a clear
-
147 // SecurityException to aid in debugging this common mistake.
-
148 // http://code.google.com/p/android/issues/detail?id=15722
-
149 if (gaiException.getCause() instanceof ErrnoException) {
-
150 if (((ErrnoException) gaiException.getCause()).errno == EACCES) {
-
151 throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException);
-
152 }
-
153 }
-
154 // Otherwise, throw an UnknownHostException.
-
155 String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error);
-
156 addressCache.putUnknownHost(host, netId, detailMessage);
-
157 throw gaiException.rethrowAsUnknownHostException(detailMessage);
-
158 }
-
159 }
上面都调用了Libcore.os.android_getaddrinfo
http://androidxref.com/9.0.0_r3/xref/libcore/luni/src/main/java/libcore/io/Libcore.java#19
-
19public final class Libcore {
-
20 private Libcore() { }
-
21
-
22 /**
-
23 * Direct access to syscalls. Code should strongly prefer using {@link #os}
-
24 * unless it has a strong reason to bypass the helpful checks/guards that it
-
25 * provides.
-
26 */
-
27 public static Os rawOs = new Linux();
-
28
-
29 /**
-
30 * Access to syscalls with helpful checks/guards.
-
31 */
-
32 public static Os os = new BlockGuardOs(rawOs);
-
33}
Os对象是一系列系统调用的抽象接口,从LibCore.java中可以看出它是通过Linux这个类实现的
http://androidxref.com/9.0.0_r3/xref/libcore/luni/src/main/java/libcore/io/Linux.java#53
-
48public final class Linux implements Os {
-
49 Linux() { }
-
53 public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
http://androidxref.com/9.0.0_r3/xref/libcore/luni/src/main/java/libcore/io/BlockGuardOs.java#164
-
40/**
-
41 * Informs BlockGuard of any activity it should be aware of.
-
42 */
-
43public class BlockGuardOs extends ForwardingOs {
-
44 public BlockGuardOs(Os os) {
-
45 super(os);
-
46 }
-
164 @Override public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException {
-
165 // With AI_NUMERICHOST flag set, the node must a numerical network address, therefore no
-
166 // host address lookups will be performed. In this case, it is fine to perform on main
-
167 // thread.
-
168 boolean isNumericHost = (hints.ai_flags & AI_NUMERICHOST) != 0;
-
169 if (!isNumericHost) {
-
170 BlockGuard.getThreadPolicy().onNetwork();
-
171 }
-
172 return os.android_getaddrinfo(node, hints, netId);
-
173 }
再找OS类
http://androidxref.com/9.0.0_r3/xref/libcore/luni/src/main/java/libcore/io/Os.java#50
-
47public interface Os {
-
50 public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
参考
得知Linux.java通过JNI (libcore_io_Linux.cpp)调用linux的posix API,那么知道了,C层的实现都在libcore_io_Linux.cpp中,咱们来看下。
http://androidxref.com/9.0.0_r3/xref/libcore/luni/src/main/native/libcore_io_Linux.cpp#1244
-
1244static jobjectArray Linux_android_getaddrinfo(JNIEnv* env, jobject, jstring javaNode,
-
1245 jobject javaHints, jint netId) {
-
1246 ScopedUtfChars node(env, javaNode);
-
1247 if (node.c_str() == NULL) {
-
1248 return NULL;
-
1249 }
-
1250
-
1251 static jfieldID flagsFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_flags", "I");
-
1252 static jfieldID familyFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_family", "I");
-
1253 static jfieldID socktypeFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_socktype", "I");
-
1254 static jfieldID protocolFid = env->GetFieldID(JniConstants::structAddrinfoClass, "ai_protocol", "I");
-
1255
-
1256 addrinfo hints;
-
1257 memset(&hints, 0, sizeof(hints));
-
1258 hints.ai_flags = env->GetIntField(javaHints, flagsFid);
-
1259 hints.ai_family = env->GetIntField(javaHints, familyFid);
-
1260 hints.ai_socktype = env->GetIntField(javaHints, socktypeFid);
-
1261 hints.ai_protocol = env->GetIntField(javaHints, protocolFid);
-
1262
-
1263 addrinfo* addressList = NULL;
-
1264 errno = 0;
-
1265 int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList);
-
1266 std::unique_ptr<addrinfo, addrinfo_deleter> addressListDeleter(addressList);
-
1267 if (rc != 0) {
-
1268 throwGaiException(env, "android_getaddrinfo", rc);
-
1269 return NULL;
-
1270 }
-
1271
-
1272 // Count results so we know how to size the output array.
-
1273 int addressCount = 0;
-
1274 for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) {
-
1275 if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
-
1276 ++addressCount;
-
1277 } else {
-
1278 ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family);
-
1279 }
-
1280 }
-
1281 if (addressCount == 0) {
-
1282 return NULL;
-
1283 }
-
1284
-
1285 // Prepare output array.
-
1286 jobjectArray result = env->NewObjectArray(addressCount, JniConstants::inetAddressClass, NULL);
-
1287 if (result == NULL) {
-
1288 return NULL;
-
1289 }
-
1290
-
1291 // Examine returned addresses one by one, save them in the output array.
-
1292 int index = 0;
-
1293 for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) {
-
1294 if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
-
1295 // Unknown address family. Skip this address.
-
1296 ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family);
-
1297 continue;
-
1298 }
-
1299
-
1300 // Convert each IP address into a Java byte array.
-
1301 sockaddr_storage& address = *reinterpret_cast<sockaddr_storage*>(ai->ai_addr);
-
1302 ScopedLocalRef<jobject> inetAddress(env, sockaddrToInetAddress(env, address, NULL));
-
1303 if (inetAddress.get() == NULL) {
-
1304 return NULL;
-
1305 }
-
1306 env->SetObjectArrayElement(result, index, inetAddress.get());
-
1307 ++index;
-
1308 }
-
1309 return result;
-
1310}
调用android_getaddrinfofornet()
http://androidxref.com/9.0.0_r3/xref/libcore/luni/src/main/native/Portability.h#28
-
28static inline int android_getaddrinfofornet(const char* hostname, const char* servname,
-
29 const struct addrinfo* hints, unsigned /*netid*/, unsigned /*mark*/, struct addrinfo** res) {
-
30 return getaddrinfo(hostname, servname, hints, res);
-
31}
最终调用到bionic/libc/dns/net/getaddrinfo.c
http://androidxref.com/9.0.0_r3/xref/bionic/libc/dns/net/getaddrinfo.c#564
-
562__BIONIC_WEAK_FOR_NATIVE_BRIDGE
-
563int
-
564getaddrinfo(const char *hostname, const char *servname,
-
565 const struct addrinfo *hints, struct addrinfo **res)
-
566{
-
567 return android_getaddrinfofornet(hostname, servname, hints, NETID_UNSET, MARK_UNSET, res);
-
568}
-
570__BIONIC_WEAK_FOR_NATIVE_BRIDGE
-
571int
-
572android_getaddrinfofornet(const char *hostname, const char *servname,
-
573 const struct addrinfo *hints, unsigned netid, unsigned mark, struct addrinfo **res)
-
574{
-
575 struct android_net_context netcontext = {
-
576 .app_netid = netid,
-
577 .app_mark = mark,
-
578 .dns_netid = netid,
-
579 .dns_mark = mark,
-
580 .uid = NET_CONTEXT_INVALID_UID,
-
581 };
-
582 return android_getaddrinfofornetcontext(hostname, servname, hints, &netcontext, res);
-
583}
-
584
-
585__BIONIC_WEAK_FOR_NATIVE_BRIDGE
-
586int
-
587android_getaddrinfofornetcontext(const char *hostname, const char *servname,
-
588 const struct addrinfo *hints, const struct android_net_context *netcontext,
-
589 struct addrinfo **res)
-
590{
-
591 struct addrinfo sentinel;
-
592 struct addrinfo *cur;
-
593 int error = 0;
-
594 struct addrinfo ai;
-
595 struct addrinfo ai0;
-
596 struct addrinfo *pai;
-
597 const struct explore *ex;
-
598
-
599 /* hostname is allowed to be NULL */
-
600 /* servname is allowed to be NULL */
-
601 /* hints is allowed to be NULL */
-
602 assert(res != NULL);
-
603 assert(netcontext != NULL);
-
604 memset(&sentinel, 0, sizeof(sentinel));
-
605 cur = &sentinel;
-
606 pai = &ai;
-
607 pai->ai_flags = 0;
-
608 pai->ai_family = PF_UNSPEC;
-
609 pai->ai_socktype = ANY;
-
610 pai->ai_protocol = ANY;
-
611 pai->ai_addrlen = 0;
-
612 pai->ai_canonname = NULL;
-
613 pai->ai_addr = NULL;
-
614 pai->ai_next = NULL;
-
615
-
616 if (hostname == NULL && servname == NULL)
-
617 return EAI_NONAME;
-
618 if (hints) {
-
619 /* error check for hints */
-
620 if (hints->ai_addrlen || hints->ai_canonname ||
-
621 hints->ai_addr || hints->ai_next)
-
622 ERR(EAI_BADHINTS); /* xxx */
-
623 if (hints->ai_flags & ~AI_MASK)
-
624 ERR(EAI_BADFLAGS);
-
625 switch (hints->ai_family) {
-
626 case PF_UNSPEC:
-
627 case PF_INET:
-
628#ifdef INET6
-
629 case PF_INET6:
-
630#endif
-
631 break;
-
632 default:
-
633 ERR(EAI_FAMILY);
-
634 }
-
635 memcpy(pai, hints, sizeof(*pai));
-
636
-
637 /*
-
638 * if both socktype/protocol are specified, check if they
-
639 * are meaningful combination.
-
640 */
-
641 if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
-
642 for (ex = explore; ex->e_af >= 0; ex++) {
-
643 if (pai->ai_family != ex->e_af)
-
644 continue;
-
645 if (ex->e_socktype == ANY)
-
646 continue;
-
647 if (ex->e_protocol == ANY)
-
648 continue;
-
649 if (pai->ai_socktype == ex->e_socktype
-
650 && pai->ai_protocol != ex->e_protocol) {
-
651 ERR(EAI_BADHINTS);
-
652 }
-
653 }
-
654 }
-
655 }
-
656
-
657 /*
-
658 * check for special cases. (1) numeric servname is disallowed if
-
659 * socktype/protocol are left unspecified. (2) servname is disallowed
-
660 * for raw and other inet{,6} sockets.
-
661 */
-
662 if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
-
663#ifdef PF_INET6
-
664 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
-
665#endif
-
666 ) {
-
667 ai0 = *pai; /* backup *pai */
-
668
-
669 if (pai->ai_family == PF_UNSPEC) {
-
670#ifdef PF_INET6
-
671 pai->ai_family = PF_INET6;
-
672#else
-
673 pai->ai_family = PF_INET;
-
674#endif
-
675 }
-
676 error = get_portmatch(pai, servname);
-
677 if (error)
-
678 ERR(error);
-
679
-
680 *pai = ai0;
-
681 }
-
682
-
683 ai0 = *pai;
-
684
-
685 /* NULL hostname, or numeric hostname */
-
686 for (ex = explore; ex->e_af >= 0; ex++) {
-
687 *pai = ai0;
-
688
-
689 /* PF_UNSPEC entries are prepared for DNS queries only */
-
690 if (ex->e_af == PF_UNSPEC)
-
691 continue;
-
692
-
693 if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
-
694 continue;
-
695 if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
-
696 continue;
-
697 if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
-
698 continue;
-
699
-
700 if (pai->ai_family == PF_UNSPEC)
-
701 pai->ai_family = ex->e_af;
-
702 if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
-
703 pai->ai_socktype = ex->e_socktype;
-
704 if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
-
705 pai->ai_protocol = ex->e_protocol;
-
706
-
707 if (hostname == NULL)
-
708 error = explore_null(pai, servname, &cur->ai_next);
-
709 else
-
710 error = explore_numeric_scope(pai, hostname, servname,
-
711 &cur->ai_next);
-
712
-
713 if (error)
-
714 goto free;
-
715
-
716 while (cur->ai_next)
-
717 cur = cur->ai_next;
-
718 }
-
719
-
720 /*
-
721 * XXX
-
722 * If numeric representation of AF1 can be interpreted as FQDN
-
723 * representation of AF2, we need to think again about the code below.
-
724 */
-
725 if (sentinel.ai_next)
-
726 goto good;
-
727
-
728 if (hostname == NULL)
-
729 ERR(EAI_NODATA);
-
730 if (pai->ai_flags & AI_NUMERICHOST)
-
731 ERR(EAI_NONAME);
-
732
-
733#if defined(__ANDROID__)
-
734 int gai_error = android_getaddrinfo_proxy(
-
735 hostname, servname, hints, res, netcontext->app_netid);
-
736 if (gai_error != EAI_SYSTEM) {
-
737 return gai_error;
-
738 }
-
739#endif
-
740
-
741 /*
-
742 * hostname as alphabetical name.
-
743 * we would like to prefer AF_INET6 than AF_INET, so we'll make a
-
744 * outer loop by AFs.
-
745 */
-
746 for (ex = explore; ex->e_af >= 0; ex++) {
-
747 *pai = ai0;
-
748
-
749 /* require exact match for family field */
-
750 if (pai->ai_family != ex->e_af)
-
751 continue;
-
752
-
753 if (!MATCH(pai->ai_socktype, ex->e_socktype,
-
754 WILD_SOCKTYPE(ex))) {
-
755 continue;
-
756 }
-
757 if (!MATCH(pai->ai_protocol, ex->e_protocol,
-
758 WILD_PROTOCOL(ex))) {
-
759 continue;
-
760 }
-
761
-
762 if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
-
763 pai->ai_socktype = ex->e_socktype;
-
764 if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
-
765 pai->ai_protocol = ex->e_protocol;
-
766
-
767 error = explore_fqdn(
-
768 pai, hostname, servname, &cur->ai_next, netcontext);
-
769
-
770 while (cur && cur->ai_next)
-
771 cur = cur->ai_next;
-
772 }
-
773
-
774 /* XXX */
-
775 if (sentinel.ai_next)
-
776 error = 0;
-
777
-
778 if (error)
-
779 goto free;
-
780 if (error == 0) {
-
781 if (sentinel.ai_next) {
-
782 good:
-
783 *res = sentinel.ai_next;
-
784 return SUCCESS;
-
785 } else
-
786 error = EAI_FAIL;
-
787 }
-
788 free:
-
789 bad:
-
790 if (sentinel.ai_next)
-
791 freeaddrinfo(sentinel.ai_next);
-
792 *res = NULL;
-
793 return error;
-
794}