使用iOS SDK解析SRV记录

时间:2014-03-19 10:22:30

标签: objective-c dns service-discovery dns-sd srv

我想使用iOS SDK解析DNS SRV记录。

我已经尝试过Apple提供的高级Bonjour API,但它们并不是我需要的。现在我正在使用DNS SD。

void *processQueryForSRVRecord(void *record) {
    DNSServiceRef sdRef;
    int context;
    printf("Setting up query for record: %s\n", record);
    DNSServiceQueryRecord(&sdRef, 0, 0, record, kDNSServiceType_SRV, kDNSServiceClass_IN, callback, &context);

    printf("Processing query for record: %s\n", record);
    DNSServiceProcessResult(sdRef);

    printf("Deallocating query for record: %s\n", record);
    DNSServiceRefDeallocate(sdRef);

    return NULL;
}

只要它只获得正确的SRV记录(例如:_xmpp-server._tcp.gmail.com),就可以正常工作,但是当记录输入错误时,DNSServiceProcessResult(sdRef)会进入无限循环。

有没有办法停止DNSServiceProcessResult或必须我取消调用它的线程?

1 个答案:

答案 0 :(得分:5)

使用好的旧select()。这就是我现在所拥有的:

- (void)updateDnsRecords
{
    if (self.dnsUpdatePending == YES)
    {
        return;
    }
    else
    {
        self.dnsUpdatePending = YES;
    }

    NSLog(@"DNS update");
    DNSServiceRef       sdRef;
    DNSServiceErrorType err;

    const char* host = [self.dnsHost UTF8String];
    if (host != NULL)
    {
        NSTimeInterval remainingTime = self.dnsUpdateTimeout;
        NSDate*        startTime = [NSDate date];

        err = DNSServiceQueryRecord(&sdRef, 0, 0,
                                    host,
                                    kDNSServiceType_SRV,
                                    kDNSServiceClass_IN,
                                    processDnsReply,
                                    &remainingTime);

        // This is necessary so we don't hang forever if there are no results
        int            dns_sd_fd = DNSServiceRefSockFD(sdRef);
        int            nfds      = dns_sd_fd + 1;
        fd_set         readfds;
        int            result;

        while (remainingTime > 0)
        {
            FD_ZERO(&readfds);
            FD_SET(dns_sd_fd, &readfds);

            struct timeval tv;
            tv.tv_sec  = (time_t)remainingTime;
            tv.tv_usec = (remainingTime - tv.tv_sec) * 1000000;

            result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
            if (result == 1)
            {
                if (FD_ISSET(dns_sd_fd, &readfds))
                {
                    err = DNSServiceProcessResult(sdRef);
                    if (err != kDNSServiceErr_NoError)
                    {
                        NSLog(@"There was an error reading the DNS SRV records.");
                        break;
                    }
                }
            }
            else if (result == 0)
            {
                NBLog(@"DNS SRV select() timed out");
                break;
            }
            else
            {
                if (errno == EINTR)
                {
                    NBLog(@"DNS SRV select() interrupted, retry.");
                }
                else
                {
                    NBLog(@"DNS SRV select() returned %d errno %d %s.", result, errno, strerror(errno));
                    break;
                }
            }

            NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:startTime];
            remainingTime -= elapsed;
        }

        DNSServiceRefDeallocate(sdRef);
    }
}


static void processDnsReply(DNSServiceRef       sdRef,
                            DNSServiceFlags     flags,
                            uint32_t            interfaceIndex,
                            DNSServiceErrorType errorCode,
                            const char*         fullname,
                            uint16_t            rrtype,
                            uint16_t            rrclass,
                            uint16_t            rdlen,
                            const void*         rdata,
                            uint32_t            ttl,
                            void*               context)
{
    NSTimeInterval* remainingTime = (NSTimeInterval*)context;

    //  If a timeout occurs the value of the errorCode argument will be
    //  kDNSServiceErr_Timeout.
    if (errorCode != kDNSServiceErr_NoError)
    {
        return;
    }

    //  The flags argument will have the kDNSServiceFlagsAdd bit set if the
    //  callback is being invoked when a record is received in response to
    //  the query.
    //
    //  If kDNSServiceFlagsAdd bit is clear then callback is being invoked
    //  because the record has expired, in which case the ttl argument will
    //  be 0.
    if ((flags & kDNSServiceFlagsMoreComing) == 0)
    {
        *remainingTime = 0;
    }

    // Record parsing code below was copied from Apple SRVResolver sample.
    NSMutableData *         rrData = [NSMutableData data];
    dns_resource_record_t * rr;
    uint8_t                 u8;
    uint16_t                u16;
    uint32_t                u32;

    u8 = 0;
    [rrData appendBytes:&u8 length:sizeof(u8)];
    u16 = htons(kDNSServiceType_SRV);
    [rrData appendBytes:&u16 length:sizeof(u16)];
    u16 = htons(kDNSServiceClass_IN);
    [rrData appendBytes:&u16 length:sizeof(u16)];
    u32 = htonl(666);
    [rrData appendBytes:&u32 length:sizeof(u32)];
    u16 = htons(rdlen);
    [rrData appendBytes:&u16 length:sizeof(u16)];
    [rrData appendBytes:rdata length:rdlen];

    rr = dns_parse_resource_record([rrData bytes], (uint32_t) [rrData length]);

    // If the parse is successful, add the results.
    if (rr != NULL)
    {
        NSString *target;

        target = [NSString stringWithCString:rr->data.SRV->target encoding:NSASCIIStringEncoding];
        if (target != nil)
        {
            uint16_t priority = rr->data.SRV->priority;
            uint16_t weight   = rr->data.SRV->weight;
            uint16_t port     = rr->data.SRV->port;

            [[FailoverWebInterface sharedInterface] addDnsServer:target priority:priority weight:weight port:port ttl:ttl];  // You'll have to do this in with your own method.
        }
    }

    dns_free_resource_record(rr);
}

Here's the Apple SRVResolver sample我从中获得了RR解析。

这个Apple示例提到它可能会永久阻止,但在尝试自己添加超时时,建议使用NSTimer。但我认为使用select()是一种更好的方法。

我有一个待办事项:实施flushing cache with DNSServiceReconfirmRecord。但现在不会这样做。

请注意,此代码正常运行,但我仍在测试它。

您需要将libresolv.dylib添加到您的Xcode项目'链接的框架和库中。