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
How to create ip address and port into NSData to work with
From the header file:
* Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object.
*
* If you have an existing struct sockaddr you can convert it to a NSData object like so:
* struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
* struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
So, you need a sockaddr
:
#include <netinet/in.h>
#include <arpa/inet.h>
struct sockaddr_in ip;
ip.sin_family = AF_INET;
ip.sin_port = htons(6003);
inet_pton(AF_INET, "0.0.0.0", &ip.sin_addr);
NSData * discoveryHost = [NSData dataWithBytes:&ip length:ip.sin_len];
Here's some documentation on sockaddr
- http://www.beej.us/guide/bgnet/output/html/multipage/sockaddr_inman.html
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
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
How to get sockaddr from sockaddr_in in Swift?
I translated the code based on your start as:
func send(_ address: String) {
let socketSD = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)
guard socketSD > 0 else { print("Error: Could not open socket."); return }
defer { close(socketSD) }
// set socket options enable broadcast
var broadcastEnable = 1
let ret = setsockopt(socketSD, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, socklen_t(MemoryLayout.size(ofValue: broadcastEnable)))
guard ret == 0 else { print("Error: Could not open set socket to broadcast mode"); return }
var broadcastAddr = sockaddr_in(sin_len: 0,
sin_family: sa_family_t(AF_INET),
sin_port: in_port_t(UInt16(1314).bigEndian),
sin_addr: in_addr(s_addr: 0),
sin_zero: (0,0,0,0,0,0,0,0))
let broadcastAddrSize = MemoryLayout.size(ofValue: broadcastAddr)
address.withCString {
address in
inet_pton(AF_INET, address, &broadcastAddr.sin_addr)
}
let result = withUnsafePointer(to: &broadcastAddr) {
return $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { aSockaddr in
return "message".withCString { message in
return sendto(socketSD, message, strlen(message), 0, aSockaddr, socklen_t(broadcastAddrSize))
}
}
}
print(result)
if result < 0 {
print("Error: Could not open send broadcast. \(result)")
}
}
If I run it, it doesn't crash and returns a positive result, but I don't know that it works properly.
Related Topics
Xcode 6 Isn't Autocompleting in Swift
Simple Way to Change the Position of Uiview
Xcode - There Are No Dsyms Available for Download
How to Draw a "Speech Bubble" on an Iphone
How to Convert Nsdate into Unix Timestamp iPhone Sdk
What Are File Owner and First Responder in iOS - Xcode
Xcode Keeps Building Storyboard After Each Keystroke
How to Prevent the iPhone Screen from Dimming or Turning Off While My Application Is Running
How to Allow User to Pick the Image with Swift
Draw a Circle of 1000M Radius Around Users Location in Mkmapview
Adjust Uibutton Font Size to Width
What Should Image Sizes Be at @1X, @2X and @3X in Xcode
The Supportedinterfaceorientations Method Doesn't Override Any Method from Its Superclass
(Swift) Storing and Retrieving Array to Nsuserdefaults
Linker Error in iOS (Duplicate Symbols for Architecture X86_64)
The Operation Couldn't Be Completed. (Com.Facebook.Sdk Error 2.) iOS6
Ld: Framework Not Found Parse Xcode 7 Beta
Xcode 10.2 with Swift 5.0 Compiler - Protocol Inheritance Issue