Meg*_*anX 5 dns objective-c ios swift
如标题所述,我具有要使用指定的DNS服务器解析的主机名(例如www.example.com)。例如,在一种情况下,我想使用Google的IPv4 DNS,在另一种情况下,则要使用Google的IPv6 DNS。
我在iOS上浏览了SO,找到了类似的问题(Swift-获取设备的IP地址),所以我确定可以做到,但是我不清楚如何做?
我怎样才能做到这一点?
编辑2018年6月7日
@mdeora从http://www.software7.com/blog/programmatically-query-specific-dns-servers-on-ios/提出的建议解决方案
此解决方案有效,但仅当我使用IPv4 DNS(例如Google的“ 8.8.8.8”)时才有效。如果我尝试使用IPv6 DNS 2001:4860:4860 :: 8888,我什么也收不到。
我设法更改了转换:
void setup_dns_server(res_state res, const char *dns_server)
{
res_ninit(res);
struct in_addr addr;
// int returnValue = inet_aton(dns_server, &addr);
inet_pton(AF_INET6, dns_server, &addr); // for IPv6 conversion
res->nsaddr_list[0].sin_addr = addr;
res->nsaddr_list[0].sin_family = AF_INET;
res->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
res->nscount = 1;
};
Run Code Online (Sandbox Code Playgroud)
但是仍然有麻烦:
void query_ip(res_state res, const char *host, char ip[])
{
u_char answer[NS_PACKETSZ];//NS_IN6ADDRSZ NS_PACKETSZ
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
if(ns_msg_count(handle, ns_s_an) > 0) {
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
strcpy(ip, inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
}
}
}
Run Code Online (Sandbox Code Playgroud)
我得到-1的len。据我看来,我似乎需要为IPv6配置res_state。
这是我的博客文章中的代码,上面已经提到过,只是稍微修改了一下以使用 IPv6。
调整setup_dns_server
首先我们可以从 setup_dns_server 的更改开始:
void setup_dns_server(res_state res, const char *dns_server) {
struct in6_addr addr;
inet_pton(AF_INET6, dns_server, &addr);
res->_u._ext.ext->nsaddrs[0].sin6.sin6_addr = addr;
res->_u._ext.ext->nsaddrs[0].sin6.sin6_family = AF_INET6;
res->_u._ext.ext->nsaddrs[0].sin6.sin6_port = htons(NS_DEFAULTPORT);
res->nscount = 1;
}
Run Code Online (Sandbox Code Playgroud)
添加 __res_state_ext
由于缺少 __res_state_ext 结构,因此无法编译。不幸的是,这个结构位于私有头文件中。
但该定义可以从这里获取: https://opensource.apple.com/source/libresolv/libresolv-65/res_private.h.auto.html:
struct __res_state_ext {
union res_sockaddr_union nsaddrs[MAXNS];
struct sort_list {
int af;
union {
struct in_addr ina;
struct in6_addr in6a;
} addr, mask;
} sort_list[MAXRESOLVSORT];
char nsuffix[64];
char bsuffix[64];
char nsuffix2[64];
};
Run Code Online (Sandbox Code Playgroud)
该结构可以添加到例如文件的顶部。
适配解析主机
此处的更改包括更长的 ip 缓冲区 (INET6_ADDRSTRLEN)。res_ninit 从 setup_dns_server 移至此方法,并且现在与 res_ndestroy 匹配。
+ (NSString *)resolveHost:(NSString *)host usingDNSServer:(NSString *)dnsServer {
struct __res_state res;
char ip[INET6_ADDRSTRLEN];
memset(ip, '\0', sizeof(ip));
res_ninit(&res);
setup_dns_server(&res, [dnsServer cStringUsingEncoding:NSASCIIStringEncoding]);
query_ip(&res, [host cStringUsingEncoding:NSUTF8StringEncoding], ip);
res_ndestroy(&res);
return [[NSString alloc] initWithCString:ip encoding:NSASCIIStringEncoding];
}
Run Code Online (Sandbox Code Playgroud)
检索 IPv6 地址
如果您只想使用 IPv6 地址作为 DNS 服务器,上述更改就足够了。因此,如果您仍想检索 IPv4 地址,则在 query_ip 中无需进行任何更改。
如果您还想从 DNS 服务器检索 IPv6 地址,您可以执行以下操作:
void query_ip(res_state res, const char *host, char ip[]) {
u_char answer[NS_PACKETSZ];
int len = res_nquery(res, host, ns_c_in, ns_t_aaaa, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
if(ns_msg_count(handle, ns_s_an) > 0) {
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
inet_ntop(AF_INET6, ns_rr_rdata(rr), ip, INET6_ADDRSTRLEN);
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意:我们在这里使用 ns_t_aaaa 来获取 AAAA 资源记录(quad-A 记录),因为在 DNS 中它指定了 IPv6 地址和主机名之间的映射。对于许多主机来说,没有这样的 quad-A 记录,这意味着您只能通过 IPv4 访问它们。
称呼
你可以这样称呼它:
NSString *resolved = [ResolveUtil resolveHost:@"www.google.com" usingDNSServer:@"2001:4860:4860::8888"];
NSLog(@"%@", resolved);
Run Code Online (Sandbox Code Playgroud)
结果看起来像这样:
免责声明
这些只是简单的示例调用,演示了函数的基本用法。没有错误处理。
| 归档时间: |
|
| 查看次数: |
821 次 |
| 最近记录: |