How to Cast Sockaddr_In to Sockaddr in Swift

How to cast sockaddr_in to sockaddr in swift 2.0?

Similarly as in the referenced Q&A, you have to use withUnsafePointer()

var sa = sockaddr_in()
let s = socket(PF_INET,SOCK_STREAM,0)

let cn = withUnsafePointer(&sa) {
connect(s, UnsafePointer($0), socklen_t(sizeofValue(sa)))
}

Note also that sizeofValue() must be used with an instance of
a type, and that the value must be converted to socklen_t
as expected by connect().

Update for Swift 3:

let cn = withUnsafeMutablePointer(to: &sa) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
connect(s, $0, socklen_t(MemoryLayout.size(ofValue: sa)))
}
}

How to cast sockadd to sockaddr_in in swift

You have to take the address of addr (which requires
addr to be a variable), use withMemoryRebound() to
temporarily rebind it to an sockaddr_in pointer, which can then
be dereferenced:

var addr: sockaddr = ...
let addr_in = withUnsafePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
$0.pointee
}
}

There are some problems in your defaultGatewayAddress() method:

  • sa = sa!.advanced(by: ROUNDUP(a: Int(sa!.pointee.sa_len))) advances sa by
    sa_len multiplied with the size of sockaddr, which is not what you intend.
  • The test if ((rt!.pointee.rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
    must be done after the for i in 0..<RTAX_MAX loop.
  • With sa_tab.insert(sa!.pointee, at: Int(i)) you insert new elements into the
    array instead of replacing them.

Note also that

rt = p!.withMemoryRebound(to: rt_msghdr.self, capacity: 1, {$0})

might work here, but it generally unsafe: The $0 pointer is only valid for
the execution of the closure and must not be passed to the outside.

Here is a working version of your code. It is essentially a translation of
How can I determine the default gateway on iPhone?
to Swift (which in turn seems it built on https://github.com/getlantern/libnatpmp/blob/master/getgateway.c).

I have also simplified it a bit and modified to avoid all forced unwraps.

func defaultGatewayAddress() -> in_addr? {

var defaultGateway: in_addr?

var mib:[Int32] = [CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY]
var len = 0
if sysctl(&mib, u_int(mib.count), nil, &len, nil, 0) < 0 {
return nil
}
let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: len)
defer {
buffer.deallocate(capacity: len)
}
if sysctl(&mib, u_int(mib.count), buffer, &len, nil, 0) < 0 {
return nil
}

var sa_tab = [UnsafePointer<sockaddr>?](repeating: nil, count: Int(RTAX_MAX))

var ptr = buffer
while ptr < buffer + len {
ptr.withMemoryRebound(to: rt_msghdr.self, capacity: 1) { rt in
var sa = UnsafeMutableRawPointer(rt + 1).assumingMemoryBound(to: sockaddr.self)
for i in 0..<RTAX_MAX {
if rt.pointee.rtm_addrs & (1 << i) != 0 {
sa_tab[Int(i)] = UnsafePointer(sa)
sa = (UnsafeMutableRawPointer(sa) + Int(sa.pointee.sa_len)).assumingMemoryBound(to: sockaddr.self)
} else {
sa_tab[Int(i)] = nil
}
}

if let dst = sa_tab[Int(RTAX_DST)],
dst.pointee.sa_family == sa_family_t(AF_INET),
let gateway = sa_tab[Int(RTAX_GATEWAY)],
gateway.pointee.sa_family == sa_family_t(AF_INET)
{
dst.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { addr in
if addr.pointee.sin_addr.s_addr == 0 {

var name = [CChar](repeating: CChar(0), count: Int(IFNAMSIZ) + 1)
if_indextoname(UInt32((rt.pointee.rtm_index)), &name)
if String(cString: name) == "en0" {
defaultGateway = gateway.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
$0.pointee.sin_addr
}
}
}
}

}
ptr += Int(rt.pointee.rtm_msglen)
}
}

return defaultGateway
}

Cast to different C struct unsafe pointer in Swift

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.

cast from sockaddr * to sockaddr_in * increases required alignment

TLDR: This warning doesn't indicate an error in your code, but you can avoid it by using a poper c++ reinterpret_cast (thanks to @Kurt Stutsman).


Explanation:

Reason for the warning:

  • sockaddr consists of a unsigned short (usually 16 bit) and a char array, so its alignment requirement is 2.
  • sockaddr_in contains (among other things) a struct in_addr which has an alignment requirement of 4 which in turn means sockaddr_in also must be aligned to a 4 Byte boundary.

For that reason, casting an arbitrary sockaddr* to an sockaddr_in* changes the alignment requirement, and accessing the object via the new pointer would even violate aliasing rules and result in undefined behavior.

Why you can ignore it:

In your case, the object, p->ai_addr is pointing to, most likely is a sockaddr_in or sockaddr_in6 object anyway (as determined by checking ai_family) and so the operation is safe. However you compiler doesn't know that and produces a warning.

It is essentially the same thing as using a static_cast to cast a pointer to a base class to a pointer to a derived class - it is unsafe in the general case, but if you know the correct dynamic type extrinsically, it is well defined.

Solution:
I don't know a clean way around this (other than suppress the warning), which is not unusual with warnings enabled by -Weverything . You could copy the object pointed to by p->ai_addr byte by byte to an object of the appropriate type, but then you could (most likely) no longer use addr the same way as before, as it would now point to a different (e.g. local) variable.
-Weverything isn't something I would use for my usual builds anyway, because it adds far too much noise, but if you want to keep it, @Kurt Stutsman mentioned a good solution in the comments:

clang++ (g++ doesn't emit a warning in any case) doesn't emit a warning, if you use a reinterpret_cast instead of the c style cast (which you shouldn't use anyway), although both have (in this case) exactly the same functionality. Maybe because reinterpret_cast explicitly tells the compiler: "Trust me, I know, what I'm doing" .


On a side Note: In c++ code you don't need the struct keywords.

Init a `sockaddr` struct with a given IP address in Swift

You create a sockaddr_in first:

var addr = sockaddr_in()
addr.sin_len = UInt8(MemoryLayout.size(ofValue: addr))
addr.sin_family = sa_family_t(AF_INET)

addr.sin_addr.s_addr = inet_addr("10.0.0.1")
// Alternatively:
inet_pton(AF_INET, "10.0.0.1", &addr.sin_addr)

and then "rebind" it to a sockaddr pointer, e.g.

let reachability = withUnsafePointer(to: &addr, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, $0)
}
})

Some explanation:

There are different address types for different address families (such as struct sockaddr_in for IPv4, struct sockaddr_in6 for IPv6). struct sockaddr is the “common part”, and many socket functions which take a pointer to a sockaddr as parameter.

In C you can simply cast between the pointers:

struct sockaddr sin;
// ...
struct sockaddr *addrPtr = (struct sockaddr *)&sin;

In Swift that is done more explicitly as “rebinding”.
For more information, see

  • SE-0107 UnsafeRawPointer API
  • UnsafeRawPointer Migration

Convert NSData to sockaddr struct in swift

For a simpler solution using getnameinfo, see Martin's answer here: How can I get a real IP address from DNS query in Swift?

Updated for Swift 5 / IPv6:

The objects returned by CFHostGetAddressing can be bridged to Swift as Data, and cast to in_addr/in6_addr by using withUnsafeBytes and assumingMemoryBound(to:).

Here's a complete example that uses inet_ntop to convert IPv4/IPv6 addresses to strings:

import CFNetwork
import Foundation

protocol NetworkAddress {
static var family: Int32 { get }
static var maxStringLength: Int32 { get }
}
extension in_addr: NetworkAddress {
static let family = AF_INET
static let maxStringLength = INET_ADDRSTRLEN
}
extension in6_addr: NetworkAddress {
static let family = AF_INET6
static let maxStringLength = INET6_ADDRSTRLEN
}

extension String {
init<A: NetworkAddress>(address: A) {
// allocate a temporary buffer large enough to hold the string
var buf = ContiguousArray<Int8>(repeating: 0, count: Int(A.maxStringLength))
self = withUnsafePointer(to: address) { rawAddr in
buf.withUnsafeMutableBufferPointer {
String(cString: inet_ntop(A.family, rawAddr, $0.baseAddress, UInt32($0.count)))
}
}
}
}

func addressToString(data: Data) -> String? {
return data.withUnsafeBytes {
let family = $0.baseAddress!.assumingMemoryBound(to: sockaddr_storage.self).pointee.ss_family
// family determines which address type to cast to (IPv4 vs IPv6)
if family == numericCast(AF_INET) {
return String(address: $0.baseAddress!.assumingMemoryBound(to: sockaddr_in.self).pointee.sin_addr)
} else if family == numericCast(AF_INET6) {
return String(address: $0.baseAddress!.assumingMemoryBound(to: sockaddr_in6.self).pointee.sin6_addr)
}
return nil
}
}

let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue()
var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil))
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [Data]?

print(addresses?.compactMap(addressToString))


You can use the NSData method getBytes(_, length:) method and pass the sockaddr struct to the inout parameter using the prefix & operator:

var data: NSData ...
var address: sockaddr ...

data.getBytes(&address, length: MemoryLayout<sockaddr>.size)

Updated for Swift 3:

let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue()
var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil))
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]?

if let data = addresses?.first {
var storage = sockaddr_storage()
data.getBytes(&storage, length: MemoryLayout<sockaddr_storage>.size)

if Int32(storage.ss_family) == AF_INET {
let addr4 = withUnsafePointer(to: &storage) {
$0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
$0.pointee
}
}

// prints 74.125.239.132
print(String(cString: inet_ntoa(addr4.sin_addr), encoding: .ascii))
}
}


Updated 6/3/2015:
Now that C structs can be easily zero-initialized, this becomes much simpler:

let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue()
var resolved = CFHostStartInfoResolution(host, .Addresses, nil)
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]?

if let data = addresses?.first {
var storage = sockaddr_storage()
data.getBytes(&storage, length: sizeof(sockaddr_storage))

if Int32(storage.ss_family) == AF_INET {
let addr4 = withUnsafePointer(&storage) { UnsafePointer<sockaddr_in>($0).memory }

// prints 74.125.239.132
println(String(CString: inet_ntoa(addr4.sin_addr), encoding: NSASCIIStringEncoding))
}
}


Unfortunately this requires sockaddr to be initialized first. To avoid that, you could do something like this:

func makeWithUnsafePointer<T>(body: UnsafePointer<T> -> ()) -> T {
let ptr = UnsafePointer<T>.alloc(sizeof(T))
body(ptr)
return ptr.move()
}

let addr: sockaddr = makeWithUnsafePointer {
data.getBytes($0 as UnsafePointer<sockaddr>, length: sizeof(sockaddr))
}

Or this:

func makeWithUninitialized<T>(body: inout T -> ()) -> T {
let ptr = UnsafePointer<T>.alloc(sizeof(T))
body(&ptr.memory)
return ptr.move()
}

let addr = makeWithUninitialized { (inout addr: sockaddr) in
data.getBytes(&addr, length: sizeof(sockaddr))
}

For more discussion, see Swift: Pass Uninitialized C Structure to Imported C function

Swift 3 - Convert c structure sockaddr_in to CFData

NSData has a convenient method to help you with this, and then you can take advantage of toll-free bridging to cast it to CFData:

let data = NSData(bytes: &sin, length: MemoryLayout<sockaddr_in>.size) as CFData


Related Topics



Leave a reply



Submit