Webber Lai
Webber Lai

Reputation: 2022

Swift 3 how to resolve NetService IP?

Just trying Bonjour in swift 3

Here is my code , I can receive the delegate

func netServiceDidResolveAddress(_ sender: NetService) {
 print("netServiceDidResolveAddress service name \(sender.name) of type \(sender.type)," +
                "port \(sender.port), addresses \(sender.addresses)")
}

And here is my result

netServiceDidResolveAddress service name Webber's Mac mini of type _myapp._tcp.,port 5678, addresses Optional([<1002162e c0a80205 00000000 00000000>, <1c1e162e 00000000 fe800000 00000000 00bce7ad 24b4b7e8 08000000>])

c0a80205 is the IP I looking for => 192.168.2.5

And the address is [Data] , Apple says

The addresses of the service. This is an NSArray of NSData instances, each of which contains a single struct sockaddr suitable for use with connect(2). In the event that no addresses are resolved for the service or the service has not yet been resolved, an empty NSArray is returned.

I still confuse why Data can't use .btyes ? As Apple says "This is an NSArray of NSData instances" But I can't use it like NSData

And how to resolve the address as readable IP string ?

I try this before , but do not get the result as I except ...

let thedata = NSData(bytes: sender.addresses, length: (sender.addresses?.count)!)
var storage = sockaddr_storage()
thedata.getBytes(&storage, length: sizeof(sockaddr_storage))

if Int32(storage.ss_family) == AF_INET {
    let addr4 = withUnsafePointer(&storage) {UnsafePointer<sockaddr_in>($0).pointee }
    print(inet_ntoa(addr4.sin_addr));
}

Any suggestion will be help , Thanks

Upvotes: 10

Views: 7592

Answers (4)

Phil Cole
Phil Cole

Reputation: 362

Here's how I did it in Swift 3.

func netServiceDidResolveAddress(_ sender: NetService) {
    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
    guard let data = sender.addresses?.first else { return }
    data.withUnsafeBytes { (pointer:UnsafePointer<sockaddr>) -> Void in
        guard getnameinfo(pointer, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
            return
        }
    }
    let ipAddress = String(cString:hostname)
    print(ipAddress)
}

Swift 5

var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
data.withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Void in
        let sockaddrPtr = pointer.bindMemory(to: sockaddr.self)
        guard let unsafePtr = sockaddrPtr.baseAddress else { return }
        guard getnameinfo(unsafePtr, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
            return
        }
    }
let ipAddress = String(cString:hostname)
print(ipAddress)

Upvotes: 16

Nick Hingston
Nick Hingston

Reputation: 8880

Edited Phil Coles answer for Swift 5.0 warning free solution:

func netServiceDidResolveAddress(_ sender: NetService) {
        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
        guard let data = sender.addresses?.first else { return }
        data.withUnsafeBytes { ptr in
            guard let sockaddr_ptr = ptr.baseAddress?.assumingMemoryBound(to: sockaddr.self) else {
                // handle error
                return
            }
            var sockaddr = sockaddr_ptr.pointee
            guard getnameinfo(sockaddr_ptr, socklen_t(sockaddr.sa_len), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
                return
            }
        }
        let ipAddress = String(cString:hostname)
        print(ipAddress)
    }

Upvotes: 10

Webber Lai
Webber Lai

Reputation: 2022

OK ... this is not a smart answer , at least I can get the readable IP

Just use this func to get IP string

let bonjourDevices = [NetService]()

let bonjourDevice = bonjourDevices[0] 

let host = self.getIPV4StringfromAddress(address:bonjourDevice.addresses!)


func getIPV4StringfromAddress(address: [Data] , port : Int ) -> String{

        let data = address.first! as NSData;

        var ip1 = UInt8(0)
        data.getBytes(&ip1, range: NSMakeRange(4, 1))

        var ip2 = UInt8(0)
        data.getBytes(&ip2, range: NSMakeRange(5, 1))

        var ip3 = UInt8(0)
        data.getBytes(&ip3, range: NSMakeRange(6, 1))

        var ip4 = UInt8(0)
        data.getBytes(&ip4, range: NSMakeRange(7, 1))

        let ipStr = String(format: "%d.%d.%d.%d:%d",ip1,ip2,ip3,ip4,port);

        return ipStr;
    }

Upvotes: 4

K&#233;vin Renella
K&#233;vin Renella

Reputation: 1007

I can't make it work with Data, but using NSData, I would use this:

extension NSData {
    func castToCPointer<T>() -> T {
        let mem = UnsafeMutablePointer<T>.allocate(capacity: MemoryLayout<T.Type>.size)
        self.getBytes(mem, length: MemoryLayout<T.Type>.size)
         return mem.move()
    }
}

So we have netServiceDidResolveAddress:

func netServiceDidResolveAddress(_ sender: NetService) {
    if let addresses = sender.addresses, addresses.count > 0 {
        for address in addresses {
            let data = address as NSData

            let inetAddress: sockaddr_in = data.castToCPointer()
            if inetAddress.sin_family == __uint8_t(AF_INET) {
                if let ip = String(cString: inet_ntoa(inetAddress.sin_addr), encoding: .ascii) {
                    // IPv4
                    print(ip)
                }
            } else if inetAddress.sin_family == __uint8_t(AF_INET6) {
                let inetAddress6: sockaddr_in6 = data.castToCPointer()
                let ipStringBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN))
                var addr = inetAddress6.sin6_addr

                if let ipString = inet_ntop(Int32(inetAddress6.sin6_family), &addr, ipStringBuffer, __uint32_t(INET6_ADDRSTRLEN)) {
                    if let ip = String(cString: ipString, encoding: .ascii) {
                        // IPv6
                        print(ip)
                    }
                }

                ipStringBuffer.deallocate(capacity: Int(INET6_ADDRSTRLEN))
            }
        }
    }
}

I am having the following result (storing ips in array before display):

["172.16.10.120", "172.16.8.251", "::", "::82c9:d9a5:2eed:1c87"]

Code inspired by https://gist.github.com/agrippa1994/d8c66a2ded74fb2dd801 written in Swift 2.3 and adapted for Swift 3.0

Upvotes: 3

Related Questions