Преобразование NSData в sockaddr struct в swift

Я пытаюсь выполнить простой поиск DNS в swift. До сих пор вот код, который у меня есть:

let hostRef = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue()
var resolved = CFHostStartInfoResolution(hostRef, CFHostInfoType.Addresses, nil)
let addresses = CFHostGetAddressing(hostRef, &resolved).takeRetainedValue() as NSArray

В этот момент каждый элемент в "адресах" NSArray является объектом CFDataRef, обертывающим структуру sockaddr.

Так как CFDataRef может быть беспошлинно подключен к NSData, я могу пропустить их так:

for address: AnyObject in addresses {
  println(address)  // address is of type NSData.
}

Пока так хорошо (я думаю). Это выводит достоверные данные, когда я запускаю его в unit test. Здесь я застрял. В моей жизни я не могу понять, как преобразовать байты в объекте NSData в структуру sockaddr.

Как я могу преобразовать адрес .bytes, который имеет тип COpaquePointer?, в c struct? Любая помощь оценивается. Я стучу головой о стену, пытаясь понять это.

Ответ 1

Вы можете использовать метод NSData getBytes(_, length:) и передать структуру sockaddr в параметр inout с помощью префикса &:

var data: NSData ...
var address: sockaddr ...

data.getBytes(&address, length: MemoryLayout<sockaddr>.size)

Обновлено для Swift 3:

let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue()
var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil))
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]?

if let data = addresses?.first {
    var storage = sockaddr_storage()
    data.getBytes(&storage, length: MemoryLayout<sockaddr_storage>.size)

    if Int32(storage.ss_family) == AF_INET {
        let addr4 = withUnsafePointer(to: &storage) {
            $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
                $0.pointee
            }
        }

        // prints 74.125.239.132
        print(String(cString: inet_ntoa(addr4.sin_addr), encoding: .ascii))
    }
}

Обновлено 6/3/2015: Теперь, когда C-структуры могут быть легко инициализированы нулями, это становится намного проще:

let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue()
var resolved = CFHostStartInfoResolution(host, .Addresses, nil)
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]?

if let data = addresses?.first {
    var storage = sockaddr_storage()
    data.getBytes(&storage, length: sizeof(sockaddr_storage))

    if Int32(storage.ss_family) == AF_INET {
        let addr4 = withUnsafePointer(&storage) { UnsafePointer<sockaddr_in>($0).memory }

        // prints 74.125.239.132
        println(String(CString: inet_ntoa(addr4.sin_addr), encoding: NSASCIIStringEncoding))
    }
}

Забастовкa >


К сожалению, для этого сначала требуется инициализировать sockaddr. Чтобы этого избежать, вы можете сделать что-то вроде этого:

func makeWithUnsafePointer<T>(body: UnsafePointer<T> -> ()) -> T {
    let ptr = UnsafePointer<T>.alloc(sizeof(T))
    body(ptr)
    return ptr.move()
}

let addr: sockaddr = makeWithUnsafePointer {
    data.getBytes($0 as UnsafePointer<sockaddr>, length: sizeof(sockaddr))
}

Или это:

func makeWithUninitialized<T>(body: inout T -> ()) -> T {
    let ptr = UnsafePointer<T>.alloc(sizeof(T))
    body(&ptr.memory)
    return ptr.move()
}

let addr = makeWithUninitialized { (inout addr: sockaddr) in
    data.getBytes(&addr, length: sizeof(sockaddr))
}

Подробнее см. Swift: передать неинициализированную структуру C в функцию Import C Забастовкa >