Convert Nsdata to Sockaddr Struct in Swift

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



Leave a reply



Submit