2014-03-19 83 views
6

我想使用iOS SDK解析DNS SRV记录。使用iOS SDK解析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或必须我取消了调用它的线程?

回答

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项目的“链接框架和库”中。

+1

它适合我。 – ssk

+0

看起来可行。唯一不应该使用'dnsHost'而是使用有问题服务的全名。例如,用于SIP SRV记录的'_sips._tcp'.yourdomain.com – malex