iOS:解析DNS SRV记录

a7qyws3x  于 2023-08-08  发布在  iOS
关注(0)|答案(3)|浏览(127)

我的iOS应用程序,我想解决域名的DNS服务记录(SRV)。
我找到了一些允许这样做的库,但响应是空的。我尝试的一个库是DNS。代码看起来像这样:

import UIKit
import DNS

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    let url = "_registration._tcp.gateway.zeeotp.com"
    print("Starting discovery")
    
    do {
    let request = Message(
        type: .query,
        authoritativeAnswer: false, 
        questions: [Question(name: url, type: .service)]
    )
    let requestData = try request.serialize()
    
    // Decoding a message
    let responseData = requestData
    let response = try Message.init(deserialize: responseData)
    print(response)
    } catch {
        print("Unexpected error: \(error).")
    }
  }

}

字符串
我得到的输出是:
DNS请求(id:0、权威答案:false,截断:false,recursionDesired:false,递归可用:false,问题:[DNS.问题(名称:“_registration._tcp.gateway.zeeotp.com.",键入:SRV,唯一:false,internetClass:答:[],当局:[],附加:[])
在我的研究过程中,我接触到了SRVResolver,但它是在Objective-C,我不知道如何使它与Swift一起工作。
我在ablove代码中是否遗漏了一些东西,使其正确工作?

sshcrbum

sshcrbum1#

在尝试了各种方法之后,我终于能够完成这项工作。正如@Paulw11的评论中提到的,库DNS没有发送实际的DNS请求,因此我没有得到想要的结果。

选项一:

我最终决定将Apple提供的Objective-C示例集成到我的Swift应用程序中。为此
1.我将SRVResolver. h和SRVResolver.m添加到我的项目中
1.已创建桥接头文件并添加:

//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#include "SRVResolver.h"

字符串
1.在目标的 Build Phases 下的 Link Binary With Libraries 部分中添加了libresolv.tbd
现在,在我的ViewController.swift中,我可以使用它来获得priorityweighthostname的值:

let url = "_registration._tcp.gateway.zeeotp.com"
print("Starting discovery")
    
let resolver = SRVResolver.init(srvName: url)
assert(resolver != nil)
resolver?.delegate = self
resolver?.start()
while !(resolver!.isFinished) {
    RunLoop.current.run(mode: .default, before: Date.distantFuture)
}
    
if(resolver?.error == nil) {
    for result in resolver?.results ?? [] {
        let dataArray = result as! NSDictionary;
            
        for (key, value) in dataArray { // loop through data items
            print("\(key) -> \(value)")
        }
    }
} else {
    print("Error: \(String(describing: resolver?.error))")
}

选项:2

我还发现了一个外部库NioDNS,它允许我们做一些DNS操作。使用这个库,我也能够检索SRV记录。在我的ViewController.swift中,我添加了以下代码:

import NIO
import DNSClient

let loop: MultiThreadedEventLoopGroup!

do {
    loop = MultiThreadedEventLoopGroup(numberOfThreads: 1)

    let client = try DNSClient.connect(on: loop).wait()
    let records = try client.getSRVRecords(from: url).wait()
    for record in records {
        print(record.resource.weight)
        print(record.resource.priority)
        print(record.resource.domainName.string)
    }
} catch {
    print("Error: \(error)")
}

gmol1639

gmol16392#

对于以后发现这一点的任何人,现在可以在没有遗留目标C SRVResolver的纯Swift中完成此操作,也没有任何第三方库。使用DNSServiceQueryRecord API:https://developer.apple.com/documentation/dnssd/1804747-dnsservicequeryrecord
请参阅如何用于SRV记录的示例代码:https://github.com/jamf/NoMAD-2/blob/main/NoMAD/SRVLookups/SRVResolver.swift
链接中有更多的细节,但要熟悉的主要部分是首先设置一个DNSServiceQueryRecordReply作为听到结果返回时的回调

let queryCallback: DNSServiceQueryRecordReply = { (sdRef, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdlen, rdata, ttl, context) -> Void in
    
    guard let context = context else { return }
    
    let request: SRVResolver = SRVResolver.bridge(context)

    if let data = rdata?.assumingMemoryBound(to: UInt8.self),
       let record = SRVRecord(data: Data.init(bytes: data, count: Int(rdlen))) {
        request.results.append(record)
    }
    
    if ((flags & kDNSServiceFlagsMoreComing) == 0) {
        request.success()
    }
}

字符串
然后调用DNSServiceQueryRecord发送请求,指定kDNSServiceType_SRV只接收SRV结果。

let result = DNSServiceQueryRecord(&self.serviceRef, kDNSServiceFlagsReturnIntermediates, UInt32(0), namec,  UInt16(kDNSServiceType_SRV),  UInt16(kDNSServiceClass_IN), queryCallback, SRVResolver.bridge(self))


最后,调用DNSServiceProcessResult开始阅读DNS查询的结果

let res = DNSServiceProcessResult(sdRef)


然后可以使用前面RecordReply回调中的rdata来解析数据

public struct SRVRecord: Codable, Equatable {
    
    let priority: Int
    let weight: Int
    let port: Int
    let target: String
    
    init?(data: Data) {
        
        var workingTarget = ""
        
        guard data.count > 8 else { return nil }
        priority = Int(data[0]) * 256 + Int(data[1])
        weight = Int(data[2]) * 256 + Int(data[3])
        port = Int(data[4]) * 256 + Int(data[5])
        
        // data[6] will always be a unicode control character
        // starting off the actual hostname, so we skip it
        
        for byte in data[7...(data.count - 1)] {
            if let char = String(data: Data([byte]), encoding: .utf8) {
                
                // strip out the unicode control characters
                // there's probably a better, more complete way
                
                if char == "\u{03}" || char == "\u{04}" || char == "\u{05}"  || char == "\0" {
                    workingTarget += "."
                } else {
                    workingTarget += char
                }
            }
        }
        target = workingTarget
    }
}

13z8s7eq

13z8s7eq3#

使用第三方库似乎矫枉过正。您可以通过URLSession实现您想要的结果。本例使用dns query tools,但还有其他可用的。

let apiKey = "your api key"

extension Dictionary : URLQueryParameterStringConvertible {
   var queryParameters: String {
      var parts: [String] = []
      for (key, value) in self {
         let part = String(format: "%@=%@",
                           String(describing: key).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!,
                           String(describing: value).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)
         parts.append(part as String)
      }
      return parts.joined(separator: "&")
   }
}

extension URL {
   func appendingQueryParameters(_ parametersDictionary : Dictionary<String, String>) -> URL {
      let URLString : String = String(format: "%@?%@", self.absoluteString, parametersDictionary.queryParameters)
      return URL(string: URLString)!
   }
}

protocol URLQueryParameterStringConvertible {
   var queryParameters: String {get}
}

enum RecordTpe: String {
   case A
   case SRV
   case MX
   case AAA
}

let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
guard var URL = URL(string: "https://dns.query.tools/v1/dns/query") else {fatalError()}

let URLParams = [
   "host": "_registration._tcp.gateway.zeeotp.com",
   "type": RecordTpe.SRV.rawValue,
]

URL = URL.appendingQueryParameters(URLParams)
var request = URLRequest(url: URL)
request.httpMethod = "GET"

request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
   if (error == nil) {
      let json = String(data: data!, encoding: .utf8)
      print(json!)
   }
   else {
      print("URL Session Task Failed: %@", error!.localizedDescription);
   }
})
task.resume()
session.finishTasksAndInvalidate()

字符串
我认为扩展最初来自优秀的Paw应用程序,所以请查看:Paw Cloud

相关问题