POSIX getaddrinfo allocates memory that must later be freed using freeaddrinfo.
See http://manpages.ubuntu.com/manpages/xenial/en/man3/getaddrinfo.3.html
To simplify the API, I've created this function:
import Foundation
enum SystemError: Swift.Error {
case getaddrinfo(Int32, Int32?)
}
public func getaddrinfo(node: String?, service: String?, hints: addrinfo?) throws -> [addrinfo] {
var err: Int32
var res: UnsafeMutablePointer<addrinfo>?
if var hints = hints {
err = getaddrinfo(node, service, &hints, &res)
} else {
err = getaddrinfo(node, service, nil, &res)
}
if err == EAI_SYSTEM {
throw SystemError.getaddrinfo(err, errno)
}
if err != 0 {
throw SystemError.getaddrinfo(err, nil)
}
defer {
freeaddrinfo(res)
}
var result = [addrinfo]()
var ai = res?.pointee
while ai != nil {
result.append(ai!)
ai = ai!.ai_next?.pointee
}
return result
}
I don't feel that the function is correct, though.
- How can the Swift memory model know that
getaddrinfoallocates memory, and that Swift should not overwrite that memory with own stuff? - How can Swift know that
freeaddrinfodeletes the whole list, and that it should copy out ai information that has been assigned to the result array?
What's the correct way to interface with getaddrinfo?
Memory allocated by
getaddrinfo(e.g. bymalloc) will not be given to any other dynamic memory allocation function in the same running process until released byfreeaddrinfo(e.g. byfree). Therefore the Swift runtime will not trample on that memory (if we assume that it has no programming errors such as wrong pointer calculations).Also
struct addrinfois a value type, sowill append a copy of the pointed-to structure to the array.
But there is still a problem. Some members of
struct addrinfoare pointerswhich may point into the memory allocated by
getaddrinfoand therefore invalid afterfreeaddrinfo, and dereferencing them after your function returns causes undefined behaviour.Therefore you must either postpone the
freeaddrinfountil the address list is not needed anymore, or copy the information. This is a bit cumbersome becauseai_addrmay point to a IPv4 or IPv6 socket address structure which have different length.The following code demonstrates how the address list can be copied to an array of
sockaddr_storagestructures (which are large enough to hold any IP address). This code has not been thoroughly tested, so use it with care.Remarks:
If you are only interested in a single address family then you can replace
sockaddr_storagebysockaddr_inorsockaddr_in6.I have made the
nodeandserviceparameters non-optional. The reason is that Swift currently has a Bug when passing more than one optional String to a C function, see Why does Swift return an unexpected pointer when converting an optional String into an UnsafePointer?.sequence()is used here to traverse the linked list instead of awhileloop.