代码之家  ›  专栏  ›  技术社区  ›  MegaManX

iOS:如何指定用于将主机名解析为IP地址的DNS?

  •  4
  • MegaManX  · 技术社区  · 6 年前

    正如标题所说,我有主机名(如www.example.com),我想用指定的dns服务器解析。例如,在一种情况下,我想使用谷歌的ipv4域名系统,在另一种情况下,谷歌的ipv6域名系统。

    我在ios上浏览过类似的东西,发现了类似这样的问题( Swift - Get device's IP Address ),所以我肯定可以做到,但我不清楚怎么做?

    我该怎么做?

    编辑日期:2018年7月6日

    @MDEORA建议的解决方案来自 http://www.software7.com/blog/programmatically-query-specific-dns-servers-on-ios/

    此解决方案有效,但仅当我使用IPv4 DNS时,例如谷歌的“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)));
            }
        }
    }
    

    我给伦打-1分。据我所知,我似乎需要为ipv6配置res_状态。

    3 回复  |  直到 6 年前
        1
  •  3
  •   Stephan Schlecht    6 年前

    这里是我的blogpost中的代码,上面已经提到过了,只是稍微修改了一下以使用ipv6。

    调整安装程序

    首先,我们可以从设置DNS服务器的更改开始:

    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;
    }
    

    添加状态扩展

    由于缺少struct resu 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已从安装dns_服务器移动到此方法,现在与res_ndestory匹配。

    + (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地址,上述更改已经足够了。因此,如果仍要检索IPv4地址,则在查询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);
            }
        }
    }
    

    请注意:我们在这里使用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);
    

    结果会是这样的:

    test output

    免责声明

    这些只是简单的示例调用,演示了函数的基本用法。没有错误处理。

        2
  •  2
  •   mdeora    6 年前

    您可以使用下面的swift代码-

    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!)
    

    在上面的代码中,通过替换 8.8.8.8

    有关objective-c ios,请参阅以下链接- https://www.software7.com/blog/programmatically-query-specific-dns-servers-on-ios/

        3
  •  1
  •   mdeora    6 年前

    以下是设置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。