Swift getnameinfo unreliable results for IPv6 - swift

I have the following extension on sockaddr:
extension sockaddr {
/// Indicates if this is an IPv4 address.
var isIPv4: Bool {
return sa_family == UInt8(AF_INET)
}
/// Indicates if this is an IPv6 address.
var isIPv6: Bool {
return sa_family == UInt8(AF_INET6)
}
/// Returns the address in string notation.
var address: String? {
var result: String = ""
var me = self
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(&me, socklen_t(me.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
result = String(cString: hostname)
}
return result
}
}
In an other part of my code I'm calling getifaddrs to get the interface addresses of the current device. The code above works fine for IPv4, but is somewhat unreliable for IPv6.
I get results like: 192.168.1.10 and fe80::e0fa:1204:100:0
When I change the line var result: String = "" to var result: String? = nil. The IPv6 addresses suddenly become fe80::, the rest is cut off.
Even weirder, when I just switch the var result and the var me = self lines like this:
extension sockaddr {
/// Indicates if this is an IPv4 address.
var isIPv4: Bool {
return sa_family == UInt8(AF_INET)
}
/// Indicates if this is an IPv6 address.
var isIPv6: Bool {
return sa_family == UInt8(AF_INET6)
}
/// Returns the address in string notation.
var address: String? {
var me = self
var result: String = ""
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(&me, socklen_t(me.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
result = String(cString: hostname)
}
return result
}
}
Then the function will only work for IPv4 addresses. The getnameinfo will return 4 (FAIL).
This is during debugging, with no optimizations that I know of. It doesn't matter if I run it on a simulator or real device.
Could someone please explain why this is happening?

The problem is that getnameinfo expects a pointer that can either be a sockaddr_in or a sockaddr_in6. The definition of the function is a bit confusing because it expects a sockaddr pointer.
Because I'm using an extension to extract the IP address a copy is being made of the memory contents. This isn't a problem for IPv4 because the size of a sockaddr_in is the same size as a sockaddr. However for IPv6, the sockaddr_in6 is larger than the sockaddr struct and some relevant information is cut off.
The order of my commands probably determined what was stored in memory at the location directly after the sockaddr address. Sometimes it would look like a proper IPv6 address, but in reality incorrect.
I've resolved this issue by moving my extension to the network interface ifaddrs:
extension ifaddrs {
/// Returns the IP address.
var ipAddress: String? {
var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
let address = ifa_addr.pointee
let result = getnameinfo(ifa_addr, socklen_t(address.sa_len), &buffer, socklen_t(buffer.count), nil, socklen_t(0), NI_NUMERICHOST)
return result == 0 ? String(cString: buffer) : nil
}
}
Thank you #MartinR finding the cause of the problem!

Related

NetService getting proper Inet type while debugging but not working from archived IPA

I am working with netsevices and using below function to Get IPV4 and IPV6 address.Getting Proper addresses while debugging or simple IPA file. But AFTER ARCHIVING I do not get proper INET Type.
This is strange issue.
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
                        bonjourIP = 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
                            bonjourIP = ip
                        }
                    }
                    
                    ipStringBuffer.deallocate(capacity: Int(INET6_ADDRSTRLEN))
                }
            }
        }
    }

Cast to different C struct unsafe pointer in Swift

I want to call the Posix socket functions socket and bind from Swift. socket is pretty easy—it takes Int32s, but bind is causing a problem, because I have a sockaddr_in pointer, but it wants a sockaddr pointer. In C, this would be a cast, like:
bind(sock, (struct sockaddr *)&sockAddress, sizeof(sockAddress))
Here's an attempt in Swift:
let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()
bind(sock, &sockAddress, UInt32(MemoryLayout<sockaddr_in>.size))
The bind line fails to compile with: cannot convert value of type 'sockaddr_in' to expected argument type 'sockaddr'
How do I cast the pointer?
You can write something like this:
withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
sockaddrInPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) {sockaddrPtr in
bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}
}
Or someone suggests this may be better:
withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
let sockaddrPtr = UnsafeRawPointer(sockaddrInPtr).assumingMemoryBound(to: sockaddr.self)
bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}
This article may be some help.
(UPDATE)
As described in the link shown by Martin R, now MemoryLayout<T>.stride and MemoryLayout<T>.size return the same value which is consistent with C's sizeof, where T is an imported C-struct. I'll keep my stride version of answer here, but that is not something "required" in this case now.
In Swift 3 you have to "rebind" the pointer
(compare SE-0107 UnsafeRawPointer API):
let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()
let result = withUnsafePointer(to: &sockAddress) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
bind(sock, $0, socklen_t(MemoryLayout<sockaddr_in>.stride))
}
}
Remarks:
The type annotations in let sock: Int32 and var sockAddress: sockaddr_in are not needed.
The memset() is not necessary because sockaddr_in() initializes
all struct members to zero.
The Swift equivalent of the C sizeof is stride (which includes
a possible struct padding), not size (which does not include the
struct padding). (This "problem" does not exist anymore.
For structs imported from C, stride and size have the same value.)
Swift 5 obsolete the withUnsafeBytes(UnsafePointer<sockaddr>), so below is what I'm doing with Swift 5:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
return withUnsafeBytes { (p: UnsafeRawBufferPointer) -> String? in
let addr = p.baseAddress?.assumingMemoryBound(to: sockaddr.self)
guard getnameinfo(addr, socklen_t(self.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
return nil
}
return String(cString: hostname)
}

How can I output this function to be a simple string?

I am trying to get access to the private IP in my app and I have the following code which gets the local IP and returns it. The issue is that it is returning a [String] not String and every time I try and use it I get an error. Here is the code (from How to get Ip address in swift):
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 ...
for (var ptr = ifaddr; ptr != nil; ptr = ptr.memory.ifa_next) {
let flags = Int32(ptr.memory.ifa_flags)
var 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(&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)
}
print(addresses)
return addresses
}
and here is how I am trying to use it:
self.privateIp.title = getIFAddresses()
However when I do this, I get an error:
Cannot assign a value of type "[String]" to a value of type "String".
If I try and cast it like this:
self.privateIp.title = getIFAddresses() as! String
I get the following error:
Cast from '[String]' to unrelated type 'String' always fails.
[String] is an array of string so it can contain more than one item.
If you want first element from the array you can use:
if let ipAdd = getIFAddresses().first {
self.privateIp.title = ipAdd
}
It's equivalent to getIFAddresses()[0] but more safety because if the array is empty call to getIFAddresses()[0] will crash your app.
You can call last to get the last one or you can enumerate all of the items inside the array.

Basic tcp/ip server in swift

I've been trying to write a simple TCP/IP server in swift but I couldn't come up with a solution. I've already tried searching on both here and the rest of the web but I couldn't find something that works for the latest swift version which I am working with:
Apple Swift version 2.1 (swiftlang-700.1.101.6 clang-700.1.76)
Target: x86_64-apple-darwin14.5.0
Operating system:
Mac OS X 10.10.5 Yosemitte
The following code has been made and it's based on this one: Socket Server Example with Swift
import Foundation
var sock_fd: Int32
var server_addr_size: Int
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if sock_fd == -1 {
print("Failure: creating socket")
exit(EXIT_FAILURE)
}
server_addr_size = sizeof(sockaddr_in)
var server_addr = NSMutableData(length: server_addr_size)!
memset(UnsafeMutablePointer<Void>(server_addr.mutableBytes), 0, server_addr_size)
var addr = UnsafeMutablePointer<sockaddr_in>(server_addr.mutableBytes)
addr.memory.sin_len = __uint8_t(server_addr_size)
addr.memory.sin_family = sa_family_t(AF_INET) // chooses IPv4
addr.memory.sin_port = 12321 // chooses the port
let bind_server = bind(sock_fd, UnsafePointer<sockaddr>(server_addr.mutableBytes), socklen_t(server_addr_size))
if bind_server == -1 {
print("Failure: binding port")
exit(EXIT_FAILURE)
}
if listen(sock_fd, 5) == -1 {
print("Failure: listening")
exit(EXIT_FAILURE)
}
var client_addr = NSMutableData(length: server_addr_size)!
memset(UnsafeMutablePointer<Void>(client_addr.mutableBytes), 0, server_addr_size)
let client_fd = accept(sock_fd, UnsafeMutablePointer<sockaddr>(client_addr.mutableBytes), UnsafeMutablePointer<socklen_t>(bitPattern: server_addr_size))
if client_fd == -1 {
print("Failure: accepting connection")
exit(EXIT_FAILURE);
}
The call to accept fails as it can be seen on the output given by the mentioned code:
Failure: accepting connection
Program ended with exit code: 1
In addition to the assistance on fixing the code, I would also like to know how I could read and write to/from the connection.
Thanks,
G Oliveira
In
UnsafeMutablePointer<socklen_t>(bitPattern: server_addr_size)
you are re-interpreting an integer variable as a pointer. That makes no
sense and crashes at runtime because the contents of the variable does
not point to valid memory. The accept() function expects the address
of a variable of type socklen_t here.
There are other problems as well, e.g. the port number in the socket address must be in big-endian byte order:
server_addr.sin_port = UInt16(12321).bigEndian // chooses the port
otherwise your program listens on port 8496 instead of 12321.
Using NSMutableData is not really necessary, Since Swift 1.2 you can
create a sockaddr_in structure with all elements set to zero
simply with:
var server_addr = sockaddr_in()
and then withUnsafePointer() can be used to get an address to the structure.
If a system call fails, the global variable errno is set to a non-zero value indicating the cause of the error. perror() can be used to print the error message corresponding to errno.
You may want to set the SO_REUSEADDR socket option to avoid
an "Address is already in use" error when binding the socket, see
Uses of SO_REUSEADDR? or
Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ? Do they mean the same across all major operating systems? for more information.
Here is a cleaned-up version of your code which works as expected
in my test:
let sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if sock_fd == -1 {
perror("Failure: creating socket")
exit(EXIT_FAILURE)
}
var sock_opt_on = Int32(1)
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt_on, socklen_t(sizeofValue(sock_opt_on)))
var server_addr = sockaddr_in()
let server_addr_size = socklen_t(sizeofValue(server_addr))
server_addr.sin_len = UInt8(server_addr_size)
server_addr.sin_family = sa_family_t(AF_INET) // chooses IPv4
server_addr.sin_port = UInt16(12321).bigEndian // chooses the port
let bind_server = withUnsafePointer(&server_addr) {
bind(sock_fd, UnsafePointer($0), server_addr_size)
}
if bind_server == -1 {
perror("Failure: binding port")
exit(EXIT_FAILURE)
}
if listen(sock_fd, 5) == -1 {
perror("Failure: listening")
exit(EXIT_FAILURE)
}
var client_addr = sockaddr_storage()
var client_addr_len = socklen_t(sizeofValue(client_addr))
let client_fd = withUnsafeMutablePointer(&client_addr) {
accept(sock_fd, UnsafeMutablePointer($0), &client_addr_len)
}
if client_fd == -1 {
perror("Failure: accepting connection")
exit(EXIT_FAILURE);
}
print("connection accepted")
The read() and write() system calls are then used to read from
and write to the accepted socket.
Just a little update for Swift 4.
import Foundation
let sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if sock_fd == -1 {
perror("Failure: creating socket")
exit(EXIT_FAILURE)
}
var sock_opt_on = Int32(1)
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt_on, socklen_t(MemoryLayout.size(ofValue: sock_opt_on)))
var server_addr = sockaddr_in()
let server_addr_size = socklen_t(MemoryLayout.size(ofValue: server_addr))
server_addr.sin_len = UInt8(server_addr_size)
server_addr.sin_family = sa_family_t(AF_INET) // chooses IPv4
server_addr.sin_port = UInt16(12321).bigEndian // chooses the port
let bind_server = withUnsafePointer(to: &server_addr) {
bind(sock_fd, UnsafeRawPointer($0).assumingMemoryBound(to: sockaddr.self), server_addr_size)
}
if bind_server == -1 {
perror("Failure: binding port")
exit(EXIT_FAILURE)
}
if listen(sock_fd, 5) == -1 {
perror("Failure: listening")
exit(EXIT_FAILURE)
}
var client_addr = sockaddr_storage()
var client_addr_len = socklen_t(MemoryLayout.size(ofValue: client_addr))
let client_fd = withUnsafeMutablePointer(to: &client_addr) {
accept(sock_fd, UnsafeMutableRawPointer($0).assumingMemoryBound(to: sockaddr.self), &client_addr_len)
}
if client_fd == -1 {
perror("Failure: accepting connection")
exit(EXIT_FAILURE);
}
To those who came to this topic looking for some help on the same subject, in addition to Martin's answer, I present, below, a small example on how I read and wrote to/from the socket:
// reading one char at a time
var buff_rcvd = CChar()
read(client_fd, &buff_rcvd, 1)
print(NSString(format:"Received: *%c*",buff_rcvd))
// writing one chat at a time
var buff_send: CChar = 65 // character "A" defined as CChar
write(client_fd, &buff_send, 1)
print(NSString(format:"Sent: *%c*",buff_send))
and we must not forget to close the connection after the code is done:
close(sock_fd)
close(client_fd)
EDIT: With FileDescriptors, we can do it even simpler!
let fileHandle = FileHandle(fileDescriptor: client_fd)
let data = fileHandle.readDataToEndOfFile()
print(String(data: data, encoding: .utf8))

How to cast sockaddr_in to sockaddr in swift

I'm trying to do some network code in swift and the type checking is giving me fits.
var sock: CInt = ...
var rin: sockaddr_in
var rlen = socklen_t(sizeof(sockaddr_in))
var buffer: CChar[] = CChar[](count: 128, repeatedValue: 0)
let len = recvfrom(sock, &buffer, 128, 0, &rin, &rlen)
The compiler complains (very cryptically) at the recvfrom about the fact that &rin is a pointer to sockaddr_in instead of sockaddr. I tried various ways to convert the pointer type, but I can't seem to get this right.
If I declare it to be a sockaddr I can get this to compile, but then I can't look at it as a sockaddr_in.
Update for Swift 3 and later, compare UnsafeRawPointer Migration:
var sock: CInt = 1234
var rin = sockaddr_in()
var rlen = socklen_t(MemoryLayout.size(ofValue: rin))
var buffer = [CChar](repeating: 0, count: 128)
let len = withUnsafeMutablePointer(to: &rin) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
recvfrom(sock, &buffer, buffer.count, 0, $0, &rlen)
}
}
Update: As of Swift 1.2 (Xcode 6.3 beta), initializing a C struct
has become much simpler:
var rin = sockaddr_in()
defines a sockaddr_in variable and initializes all elements to zero.
The conversion of a address of sockaddr_in to an an address of
sockaddr is done as follows:
let len = withUnsafeMutablePointer(&rin) {
recvfrom(sock, &buffer, UInt(buffer.count), 0, UnsafeMutablePointer($0), &rlen)
}
Old answer: The first problem is that the sockaddr_in variable has to be initialized before
its address can be passed to recvfrom(). Initializing complex structs in Swift
seems to be clumsy, compare
Swift: Pass Uninitialized C Structure to Imported C function.
Using the helper function from
https://stackoverflow.com/a/24335355/1187415:
func initStruct<S>() -> S {
let struct_pointer = UnsafeMutablePointer<S>.alloc(1)
let struct_memory = struct_pointer.move()
struct_pointer.dealloc(1)
return struct_memory
}
the socket address can be initialized as
var rin : sockaddr_in = initStruct()
To cast the sockaddr_in pointer to a sockaddr pointer, use reinterpretCast().
let len = recvfrom(sock, &buffer, UInt(buffer.count), 0, reinterpretCast(&rin), &rlen)