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 requiresaddr
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)))
advancessa
bysa_len
multiplied with the size ofsockaddr
, 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 thefor 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) astruct in_addr
which has an alignment requirement of 4 which in turn meanssockaddr_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
Xcode Error: Ar Reference Image Must Have Non Zero Positive Width
Nsjsonserialization Error. Code=3840 "Invalid Value Around Character 0
Filtering Realm Objects with Swift
Weak Reference to Closure in Swift
How to Get Reliable Timing for My Audio App
Picker Not Working When Editmode Is Active
How to Check If Optional Is Not Nil and Property Is True in One Expression
Nscollectionview Selection Handling in Swift
Xcode Issue: Library Not Loaded: @Rpath/Libswiftappkit.Dylib
iOS 12 Wkwebview Not Working with Redirects
Why Does Cabasicanimation Try to Initialize Another Instance of My Custom Calayer
Why Would One Use Nested Classes
Type 'Bundle' Has No Member "Module"
How Is Commoncrypto Used in Swift3
Convert from Nsdictionary to [String:Any]
Combined Watch Os and iOS Framework
Dynamic Dispatching Protocol Extension Doesn't Work Multiple Targets