[英]iOS: How to specify DNS to be used to resolve hostname to IP address?
正如标题所示,我想要使用指定的DNS服务器解析主机名(例如www.example.com)。 例如,在一种情况下,我想使用谷歌的IPv4 DNS,在其他情况下,谷歌的IPv6 DNS。
我在iOS上浏览了类似这样的东西,并发现了类似这样的问题( Swift - 获取设备的IP地址 ),所以我确信它可以完成,但我不清楚如何?
我怎样才能做到这一点?
编辑06/07/2018
@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;
};
但仍然有这个问题:
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)));
}
}
}
len得到-1。 从我收集的内容来看,我似乎需要为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;
}
添加__res_state_ext
由于缺少struct __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];
};
结构可以添加到例如文件的顶部。
适应resolveHost
这里的更改包括更长的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];
}
检索IPv6地址
如果您只想为DNS服务器使用IPv6地址,则上述更改已足够。 因此,在query_ip中,如果您仍想检索IPv4地址,则无需进行任何更改。
如果您还想从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);
}
}
}
请注意:我们在这里使用ns_t_aaaa来获取AAAA资源记录(quad-A记录),因为在DNS中它指定了IPv6地址和主机名之间的映射。 对于许多主机,没有这样的四A记录,这意味着您可以通过IPv4访问它们。
呼叫
你可以这样称呼它:
NSString *resolved = [ResolveUtil resolveHost:@"www.google.com" usingDNSServer:@"2001:4860:4860::8888"];
NSLog(@"%@", resolved);
结果如下:
放弃
这些只是简单的示例调用,演示了函数的基本用法。 没有错误处理。
您可以使用以下快速代码执行此操作 -
import Foundation
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = ["dig", "@8.8.8.8", "google.com"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
print(output!)
在上面的代码中,使用您选择的DNS服务器替换8.8.8.8
对于Objective-C iOS,请参阅以下链接 - https://www.software7.com/blog/programmatically-query-specific-dns-servers-on-ios/
以下是设置dns的修订代码 -
void setup_dns_server(res_state res, const char *dns_server)
{
res_ninit(res);
struct in_addr6 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_INET6;
res->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
res->nscount = 1;
};
和查询代码 -
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_addr6 *)ns_rr_rdata(rr)));
}
}
}
PS - 我无法测试它,但它应该适用于ipv6 dns。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.