简体   繁体   中英

Swift obtaining ip address from socket returns weird value

I'm trying to obtain the remote ip address using getpeername() under iOS/Swift (real hardware no emulation).

This is what I'm doing:

var addr = sockaddr(sa_len: 0, sa_family: 0, sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
            var len: socklen_t = socklen_t(sizeof(Int32))

                if getpeername(socket, &addr, &len) != -1
                {
                var ipAddressString = [CChar](count:Int(INET_ADDRSTRLEN), repeatedValue: 0)
                inet_ntop(
                    AF_INET ,
                    &addr,
                    &ipAddressString,
                    socklen_t(INET_ADDRSTRLEN))
                println("socket \(socket) ip \(String.fromCString(ipAddressString))")
             }

the values that I'm getting are:

socket 7 ip Optional("16.2.209.237")

and that is certainly not the remote address. Please can someone help me? What I'm doing wrong?

The main error is that inet_ntop() takes the address of a struct in_addr (or struct in_addr6 for IPv6), and not the address of a struct sockaddr .

Another error is that the length argument to getpeername() must be the length of the passed socket address structure, you are passing the length of an Int32 .

Your current code passes AF_INET to inet_ntop() and is therefore limited to IPv4 addresses. If that is sufficient for you, the following should work:

var addr = UnsafeMutablePointer<sockaddr_in>.alloc(1)
var len = socklen_t(sizeofValue(addr.memory))

if getpeername(sockfd, UnsafeMutablePointer(addr), &len) != -1 {
    var ipAddressString = [CChar](count:Int(INET_ADDRSTRLEN), repeatedValue: 0)
    inet_ntop(
        AF_INET ,
        &addr.memory.sin_addr, // <-- main difference here
        &ipAddressString,
        socklen_t(INET_ADDRSTRLEN))
    println("socket \(sockfd) ip \(String.fromCString(ipAddressString)!)")
}
addr.dealloc(1)

Allocating the socket address structure makes casting between the different pointer types a bit easier. I have also replaced the variable name socket by sockfd to avoid confusion with the socket() function.

The more "modern" function to convert socket addresses to strings is getnameinfo() . The following code demonstrates how to use it. It works for both IPv4 and IPv6 addresses:

var addr = UnsafeMutablePointer<sockaddr_storage>.alloc(1)
var len = socklen_t(sizeofValue(addr.memory))

if getpeername(sockfd, UnsafeMutablePointer(addr), &len) != -1 {

    var hostBuffer = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
    if (getnameinfo(UnsafeMutablePointer(addr), socklen_t(addr.memory.ss_len),
        &hostBuffer, socklen_t(hostBuffer.count), nil, 0,
        NI_NUMERICHOST) == 0) {
            let host = String.fromCString(hostBuffer)!
            println("socket \(sockfd) ip \(host)")
    }
}
addr.dealloc(1)

Swift 2 update: First method:

var addr = sockaddr_in()
var len = socklen_t(sizeofValue(addr))

withUnsafeMutablePointer(&addr) {
    if getpeername(sockfd, UnsafeMutablePointer($0), &len) != -1 {
        var ipAddressString = [CChar](count:Int(INET_ADDRSTRLEN), repeatedValue: 0)
        inet_ntop(
            AF_INET ,
            &addr.sin_addr, // <-- main difference here
            &ipAddressString,
            socklen_t(INET_ADDRSTRLEN))
        print("socket \(sockfd) ip \(String.fromCString(ipAddressString)!)")
    }
}

Second method:

var addr = sockaddr_storage()
var len = socklen_t(sizeofValue(addr))

withUnsafeMutablePointer(&addr) {
    if getpeername(sockfd, UnsafeMutablePointer($0), &len) != -1 {
        var hostBuffer = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
        if (getnameinfo(UnsafeMutablePointer($0), socklen_t(addr.ss_len),
            &hostBuffer, socklen_t(hostBuffer.count), nil, 0,
            NI_NUMERICHOST) == 0) {
                let host = String.fromCString(hostBuffer)!
                print("socket \(sockfd) ip \(host)")
        }
    }
}

apple sample code https://developer.apple.com/library/ios/samplecode/SimpleTunnel/Listings/tunnel_server_UDPServerConnection_swift.html

func getEndpointFromSocketAddress(socketAddressPointer: UnsafePointer<sockaddr>) -> (host: String, port: Int)? {
    let socketAddress = UnsafePointer<sockaddr>(socketAddressPointer).memory

    switch Int32(socketAddress.sa_family) {
        case AF_INET:
            var socketAddressInet = UnsafePointer<sockaddr_in>(socketAddressPointer).memory
            let length = Int(INET_ADDRSTRLEN) + 2
            var buffer = [CChar](count: length, repeatedValue: 0)
            let hostCString = inet_ntop(AF_INET, &socketAddressInet.sin_addr, &buffer, socklen_t(length))
            let port = Int(UInt16(socketAddressInet.sin_port).byteSwapped)
            return (String.fromCString(hostCString)!, port)

        case AF_INET6:
            var socketAddressInet6 = UnsafePointer<sockaddr_in6>(socketAddressPointer).memory
            let length = Int(INET6_ADDRSTRLEN) + 2
            var buffer = [CChar](count: length, repeatedValue: 0)
            let hostCString = inet_ntop(AF_INET6, &socketAddressInet6.sin6_addr, &buffer, socklen_t(length))
            let port = Int(UInt16(socketAddressInet6.sin6_port).byteSwapped)
            return (String.fromCString(hostCString)!, port)

        default:
            return nil
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM