Swift Getnameinfo Unreliable Results for Ipv6

How to get Ip address in swift

As it turned out in the discussion, OP needs the interface address on a Mac and not on an iOS device as I thought initially. The code referenced in the question checks for the
interface name "en0", which is the WiFi interface on the iPhone. On a Mac it makes more
sense to check for any "up-and-running" interface instead.
Therefore I have rewritten the answer. It is now a Swift translation of the code in
Detect any connected network.


getifaddrs() is defined in <ifaddrs.h>, which is not included by default.
Therefore you have to create a bridging header and add

#include <ifaddrs.h>

The following function returns
an array with the names of all local "up-and-running" network interfaces.

func getIFAddresses() -> [String] {
var addresses = [String]()

// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
if getifaddrs(&ifaddr) == 0 {

// For each interface ...
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr.memory.ifa_next }

let flags = Int32(ptr.memory.ifa_flags)
let addr = ptr.memory.ifa_addr.memory

// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {

// Convert interface address to a human readable string:
var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
if (getnameinfo(ptr.memory.ifa_addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
if let address = String.fromCString(hostname) {
addresses.append(address)
}
}
}
}
}
freeifaddrs(ifaddr)
}

return addresses
}

Update for Swift 3: In addition to adopting the code to the
many changes in Swift 3,
iterating over all interfaces can now use the new generalized
sequence() function:

func getIFAddresses() -> [String] {
var addresses = [String]()

// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return [] }
guard let firstAddr = ifaddr else { return [] }

// For each interface ...
for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let flags = Int32(ptr.pointee.ifa_flags)
let addr = ptr.pointee.ifa_addr.pointee

// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {

// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if (getnameinfo(ptr.pointee.ifa_addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let address = String(cString: hostname)
addresses.append(address)
}
}
}
}

freeifaddrs(ifaddr)
return addresses
}

Swift obtaining ip address from socket returns weird value

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)")
}
}
}

Getting an IP address and port number from a sockaddr_in struct in Swift?

If you need the IP address and port as numbers then you can
access the corresponding fields of a sockaddr_in directly, but
remember to convert the values from network byte (big endian) to host
byte order:

let saddr: sockAddr = ...

let port = in_port_t(bigEndian: sockAddr.sin_port)
let addr = in_addr_t(bigEndian: sockAddr.sin_addr.s_addr)

getnameinfo() can be used to extract the IP address as a string
(in dotted-decimal notation), and optionally the port as well.
Casting a struct sockaddr_in pointer to a struct sockaddr pointer
is called "rebinding" in Swift, and done with withMemoryRebound():

var sockAddr: sockaddr_in = ...

var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
var service = [CChar](repeating: 0, count: Int(NI_MAXSERV))

withUnsafePointer(to: &sockAddr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 0) {
_ = getnameinfo($0, socklen_t($0.pointee.sa_len),
&hostname, socklen_t(hostname.count),
&service, socklen_t(service.count),
NI_NUMERICHOST | NI_NUMERICSERV)
}
}
print("addr:", hostname)
print("port:", service)

This works for both IPv4 and IPv6 socket address structures (sockaddr_in and sockaddr_in6).

For more information about "unsafe pointer conversions", see
SE-0107 UnsafeRawPointer API
and UnsafeRawPointer Migration. The latter page contains example code how to handle
socket addresses in Swift 3.



Related Topics



Leave a reply



Submit