2014-03-19 10 views
6

Ich möchte DNS-SRV-Einträge mit dem iOS SDK auflösen.Auflösen von SRV-Einträgen mit iOS SDK

Ich habe bereits versucht, die High-Level-Bonjour APIs Apple bietet, aber sie sind nicht das, was ich brauche. Jetzt benutze ich 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; 
} 

Diese so lange funktioniert, wie es nur richtig SRV-Datensätze (zum Beispiel: _xmpp-server._tcp.gmail.com) erhält, aber wenn der Datensatz falsch eingegeben hat, DNSServiceProcessResult (SDref) geht in eine Endlosschleife .

Gibt es eine Möglichkeit, DNSServiceProcessResult zu stoppen oder muss ich den Thread abbrechen, der es aufruft?

Antwort

5

Verwenden Sie gute alte select(). Dies ist, was ich im Moment haben:

- (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 aus dem ich die RR-Analyse einsehen.

Diese Apple-Probe erwähnt, dass es für immer blockieren kann, aber seltsam genug vorschlagen, NSTimer zu verwenden, wenn Sie versuchen, ein Timeout selbst hinzuzufügen. Aber ich denke, mit select() ist ein viel besserer Weg.

Ich habe eine Aufgabe: Implementieren Sie flushing cache with DNSServiceReconfirmRecord. Aber werde das jetzt nicht tun.

Beachten Sie, dieser Code funktioniert, aber ich teste es noch.

Sie müssen libresolv.dylib zu den verknüpften Frameworks und Bibliotheken Ihres Xcode-Projekts hinzufügen.

+1

Es funktioniert für mich. – ssk

+0

Sieht funktionstüchtig aus. Die einzige Sache ist, sollte man nicht "dnsHost", sondern den vollständigen Namen des Dienstes verwenden. Zum Beispiel '_sips._tcp'.yourdomain.com für den SIP SRV-Eintrag – malex