Canonical way to cast C structs


#1

Hi all,

Inside my CFSocketCallBack a pointer to a sockaddr is provided wrapped in a CFData structure. The sockaddr could be either a sockaddr_in (IPv4) or sockaddr_in6 (IPv6) struct. In order to discover which struct you’re dealing with, the attribute sa_family can be inspected. In C this would look something like this:

struct sockaddr_storage sa;

switch (((sockaddr*)&sa)->sa_family)
{
    case AF_INET:
        inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
        break;
    case AF_INET6:
        inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
        break;
}
(from: http://stackoverflow.com/a/13167913)

Wheras to do this in Swift 3, the only way I found thus far is this:

var generic = CFDataGetBytePtr(data).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee
}
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
case sa_family_t(AF_INET6):
    let ipv6 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
default:
    //…
}

This looks very ugly with all the closures and ``withMemoryRebound``. Is there a better way to do this? I think there should be something more “Swifty”.

···


Bouke


#2

Ah the missing part of the puzzle appears to be unsafeBitCast(:to:), so the Swift version becomes this:

switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}

···


Bouke

On 12 sep. 2016, at 21:25, Bouke Haarsma <bouke@haarsma.eu> wrote:

Hi all,

Inside my CFSocketCallBack a pointer to a sockaddr is provided wrapped in a CFData structure. The sockaddr could be either a sockaddr_in (IPv4) or sockaddr_in6 (IPv6) struct. In order to discover which struct you’re dealing with, the attribute sa_family can be inspected. In C this would look something like this:

struct sockaddr_storage sa;

switch (((sockaddr*)&sa)->sa_family)
{
    case AF_INET:
        inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
        break;
    case AF_INET6:
        inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
        break;
}
(from: http://stackoverflow.com/a/13167913)

Wheras to do this in Swift 3, the only way I found thus far is this:

var generic = CFDataGetBytePtr(data).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee
}
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
case sa_family_t(AF_INET6):
    let ipv6 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
default:
    //…
}

This looks very ugly with all the closures and ``withMemoryRebound``. Is there a better way to do this? I think there should be something more “Swifty”.


Bouke


#3

Sorry, missed the first line when copying:

let generic = unsafeBitCast(CFDataGetBytePtr(data), to: sockaddr.self)
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}

···


Bouke

On 12 sep. 2016, at 21:35, Bouke Haarsma <bouke@haarsma.eu> wrote:

Ah the missing part of the puzzle appears to be unsafeBitCast(:to:), so the Swift version becomes this:

switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:25, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Hi all,

Inside my CFSocketCallBack a pointer to a sockaddr is provided wrapped in a CFData structure. The sockaddr could be either a sockaddr_in (IPv4) or sockaddr_in6 (IPv6) struct. In order to discover which struct you’re dealing with, the attribute sa_family can be inspected. In C this would look something like this:

struct sockaddr_storage sa;

switch (((sockaddr*)&sa)->sa_family)
{
    case AF_INET:
        inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
        break;
    case AF_INET6:
        inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
        break;
}
(from: http://stackoverflow.com/a/13167913)

Wheras to do this in Swift 3, the only way I found thus far is this:

var generic = CFDataGetBytePtr(data).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee
}
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
case sa_family_t(AF_INET6):
    let ipv6 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
default:
    //…
}

This looks very ugly with all the closures and ``withMemoryRebound``. Is there a better way to do this? I think there should be something more “Swifty”.


Bouke


#4

Sorry for all the pings, but it appears that the code below doesn’t work after all;

fatal error: can't unsafeBitCast between types of different sizes

So the question remains on how to perform the casts using Swift?

···


Bouke

On 12 sep. 2016, at 21:37, Bouke Haarsma <bouke@haarsma.eu> wrote:

Sorry, missed the first line when copying:

let generic = unsafeBitCast(CFDataGetBytePtr(data), to: sockaddr.self)
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:35, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Ah the missing part of the puzzle appears to be unsafeBitCast(:to:), so the Swift version becomes this:

switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:25, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Hi all,

Inside my CFSocketCallBack a pointer to a sockaddr is provided wrapped in a CFData structure. The sockaddr could be either a sockaddr_in (IPv4) or sockaddr_in6 (IPv6) struct. In order to discover which struct you’re dealing with, the attribute sa_family can be inspected. In C this would look something like this:

struct sockaddr_storage sa;

switch (((sockaddr*)&sa)->sa_family)
{
    case AF_INET:
        inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
        break;
    case AF_INET6:
        inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
        break;
}
(from: http://stackoverflow.com/a/13167913)

Wheras to do this in Swift 3, the only way I found thus far is this:

var generic = CFDataGetBytePtr(data).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee
}
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
case sa_family_t(AF_INET6):
    let ipv6 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
default:
    //…
}

This looks very ugly with all the closures and ``withMemoryRebound``. Is there a better way to do this? I think there should be something more “Swifty”.


Bouke


(Quinn “The Eskimo!”) #5

In general it’s best to avoid special casing your network protocols. The best option for doing that is <x-man-page://3/getaddrinfo> and <x-man-page://3/getnameinfo> calls. Specifically, with `getnameinfo`, if you specify `NI_NUMERICHOST` it’ll give you back an string representation of the address without hitting the DNS. Better yet, it starts with a (const sockaddr *), so you don’t need to extract the IP address from your CFData, you just need the base address of that data as the right type, and the `Data` type makes that easy.

So, assuming `addrCF` is your CFData, here’s how to get a string representation.

let addr = addrCF as NSData as Data
let saLen = socklen_t(addr.count)
let addrStr = addr.withUnsafeBytes { (sa: UnsafePointer<sockaddr>) -> String in
    var addrStrC = [CChar](repeating: 0, count: Int(NI_MAXHOST))
    let err = getnameinfo(sa, saLen, &addrStrC, saLen, nil, 0, NI_NUMERICHOST | NI_NUMERICSERV)
    guard err == 0 else { fatalError() }
    return String(utf8String: addrStrC)!
}

Share and Enjoy

···

On 12 Sep 2016, at 20:39, Bouke Haarsma via swift-users <swift-users@swift.org> wrote:

So the question remains on how to perform the casts using Swift?

--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware


(Andrew Trick) #6

Sorry for all the pings, but it appears that the code below doesn’t work after all;

fatal error: can't unsafeBitCast between types of different sizes

So the question remains on how to perform the casts using Swift?


Bouke

Hi Bouke,

Please see this migration guide:
https://swift.org/migration-guide/se-0107-migrate.html

It explains a few things that are not self-explanatory and includes some helper code for dealing with the socket API.

Given how your code is structured, I would start with a raw pointer, then replace all the ‘withMemoryRebound’ calls to ‘bindMemory’, and you should be fine:

var rawSockAddr = UnsafeRawPointer(CFDataGetBytePtr(data))

switch …
case …:
  let ipv4 = rawSockAddr.bindMemory(to: sockaddr_in.self, capacity: 1)

You might also be able to avoid CFData and use Swift’s Data directly, but any Swifty API you use will encourage you to restructure your code so that pointer access is confined to a closure, like Data.withUnsafeBytes, or UnsafePointer.withMemoryRebound(to:capacity:). You should never return the pointer argument from these closure. The closure taking APIs are designed to keep your data alive while you access it and make sure you’re not mixing pointers of different types to the same memory.

-Andy

···

On Sep 12, 2016, at 12:39 PM, Bouke Haarsma via swift-users <swift-users@swift.org> wrote:

On 12 sep. 2016, at 21:37, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Sorry, missed the first line when copying:

let generic = unsafeBitCast(CFDataGetBytePtr(data), to: sockaddr.self)
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:35, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Ah the missing part of the puzzle appears to be unsafeBitCast(:to:), so the Swift version becomes this:

switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:25, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Hi all,

Inside my CFSocketCallBack a pointer to a sockaddr is provided wrapped in a CFData structure. The sockaddr could be either a sockaddr_in (IPv4) or sockaddr_in6 (IPv6) struct. In order to discover which struct you’re dealing with, the attribute sa_family can be inspected. In C this would look something like this:

struct sockaddr_storage sa;

switch (((sockaddr*)&sa)->sa_family)
{
    case AF_INET:
        inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
        break;
    case AF_INET6:
        inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
        break;
}
(from: http://stackoverflow.com/a/13167913)

Wheras to do this in Swift 3, the only way I found thus far is this:

var generic = CFDataGetBytePtr(data).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee
}
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
case sa_family_t(AF_INET6):
    let ipv6 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
default:
    //…
}

This looks very ugly with all the closures and ``withMemoryRebound``. Is there a better way to do this? I think there should be something more “Swifty”.


Bouke

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


#7

Thanks for your replies. After looking at the helper code you mentioned, it appears that the code in each call will create a mutable copy of a sockaddr_storage. That mutable copy is than rebound/casted to whatever type requested. While it probably would be safer to use, it introduces extra copying overhead as well. The code that I have now (thanks to Quinn’s helper methods):

let address: sockaddr_storage
let family = CFDataGetBytePtr(addressData!).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee.sa_family
}
switch family {
case sa_family_t(AF_INET):
    (_, address) = sockaddr_storage.fromSockAddr { (sin: inout sockaddr_in) in
        sin.sin_family = sa_family_t(AF_INET)
        sin.sin_addr = CFDataGetBytePtr(addressData!).withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee.sin_addr
        }
    }
case sa_family_t(AF_INET6):
    (_, address) = sockaddr_storage.fromSockAddr { (sin: inout sockaddr_in6) in
        sin.sin6_family = sa_family_t(AF_INET6)
        sin.sin6_addr = CFDataGetBytePtr(addressData!).withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee.sin6_addr
        }
    }
default:
    return
}

Is this as Swifty as it can get? Is there a way to make it less verbose? The problem I see with this code is that the network protocols are still special-cased.

The addressData CFData object is described like this:

A CFData object holding the contents of a struct sockaddr appropriate for the protocol family of s (struct sockaddr_in or struct sockaddr_in6, for example), identifying the remote address to which s is connected. This value is NULL except for kCFSocketAcceptCallBack and kCFSocketDataCallBack callbacks.

Thanks,
Bouke

···

On 14 sep. 2016, at 08:34, Andrew Trick <atrick@apple.com> wrote:

On Sep 12, 2016, at 12:39 PM, Bouke Haarsma via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Sorry for all the pings, but it appears that the code below doesn’t work after all;

fatal error: can't unsafeBitCast between types of different sizes

So the question remains on how to perform the casts using Swift?


Bouke

Hi Bouke,

Please see this migration guide:
https://swift.org/migration-guide/se-0107-migrate.html

It explains a few things that are not self-explanatory and includes some helper code for dealing with the socket API.

Given how your code is structured, I would start with a raw pointer, then replace all the ‘withMemoryRebound’ calls to ‘bindMemory’, and you should be fine:

var rawSockAddr = UnsafeRawPointer(CFDataGetBytePtr(data))

switch …
case …:
  let ipv4 = rawSockAddr.bindMemory(to: sockaddr_in.self, capacity: 1)

You might also be able to avoid CFData and use Swift’s Data directly, but any Swifty API you use will encourage you to restructure your code so that pointer access is confined to a closure, like Data.withUnsafeBytes, or UnsafePointer.withMemoryRebound(to:capacity:). You should never return the pointer argument from these closure. The closure taking APIs are designed to keep your data alive while you access it and make sure you’re not mixing pointers of different types to the same memory.

-Andy

On 12 sep. 2016, at 21:37, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Sorry, missed the first line when copying:

let generic = unsafeBitCast(CFDataGetBytePtr(data), to: sockaddr.self)
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:35, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Ah the missing part of the puzzle appears to be unsafeBitCast(:to:), so the Swift version becomes this:

switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:25, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Hi all,

Inside my CFSocketCallBack a pointer to a sockaddr is provided wrapped in a CFData structure. The sockaddr could be either a sockaddr_in (IPv4) or sockaddr_in6 (IPv6) struct. In order to discover which struct you’re dealing with, the attribute sa_family can be inspected. In C this would look something like this:

struct sockaddr_storage sa;

switch (((sockaddr*)&sa)->sa_family)
{
    case AF_INET:
        inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
        break;
    case AF_INET6:
        inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
        break;
}
(from: http://stackoverflow.com/a/13167913)

Wheras to do this in Swift 3, the only way I found thus far is this:

var generic = CFDataGetBytePtr(data).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee
}
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
case sa_family_t(AF_INET6):
    let ipv6 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
default:
    //…
}

This looks very ugly with all the closures and ``withMemoryRebound``. Is there a better way to do this? I think there should be something more “Swifty”.


Bouke

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


#8

The Socket API Helper (https://swift.org/migration-guide/se-0107-migrate.html#socket-api-helper) expect a property ss_len on sockaddr_storage. This property is not available on Linux, so the wrappers won’t work without any additional knowledge of the internet protocol family. Any ideas on how these wrappers can be fixed for Linux?

    func withSockAddr<ReturnType>(_ body: (_ sa: UnsafePointer<sockaddr>, _ saLen: socklen_t) throws -> ReturnType) rethrows -> ReturnType {
        // We need to create a mutable copy of `self` so that we can pass it to `withUnsafePointer(to:_:)`.
        var ss = self
        return try withUnsafePointer(to: &ss) {
            try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                try body($0, socklen_t(self.ss_len))
                                       ^^^^^^^^^^^
            }
        }
    }

Replacing self.ss_len with MemoryLayout<self>.size would not be correct, as it should not be sockaddr's / sockaddr_storage’s size, but sockaddr_in[6]’s size.

···

On 17 Sep 2016, at 07:28, Bouke Haarsma <bouke@haarsma.eu> wrote:

Thanks for your replies. After looking at the helper code you mentioned, it appears that the code in each call will create a mutable copy of a sockaddr_storage. That mutable copy is than rebound/casted to whatever type requested. While it probably would be safer to use, it introduces extra copying overhead as well. The code that I have now (thanks to Quinn’s helper methods):

let address: sockaddr_storage
let family = CFDataGetBytePtr(addressData!).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee.sa_family
}
switch family {
case sa_family_t(AF_INET):
    (_, address) = sockaddr_storage.fromSockAddr { (sin: inout sockaddr_in) in
        sin.sin_family = sa_family_t(AF_INET)
        sin.sin_addr = CFDataGetBytePtr(addressData!).withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee.sin_addr
        }
    }
case sa_family_t(AF_INET6):
    (_, address) = sockaddr_storage.fromSockAddr { (sin: inout sockaddr_in6) in
        sin.sin6_family = sa_family_t(AF_INET6)
        sin.sin6_addr = CFDataGetBytePtr(addressData!).withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee.sin6_addr
        }
    }
default:
    return
}

Is this as Swifty as it can get? Is there a way to make it less verbose? The problem I see with this code is that the network protocols are still special-cased.

The addressData CFData object is described like this:

A CFData object holding the contents of a struct sockaddr appropriate for the protocol family of s (struct sockaddr_in or struct sockaddr_in6, for example), identifying the remote address to which s is connected. This value is NULL except for kCFSocketAcceptCallBack and kCFSocketDataCallBack callbacks.

Thanks,
Bouke

On 14 sep. 2016, at 08:34, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Sep 12, 2016, at 12:39 PM, Bouke Haarsma via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Sorry for all the pings, but it appears that the code below doesn’t work after all;

fatal error: can't unsafeBitCast between types of different sizes

So the question remains on how to perform the casts using Swift?


Bouke

Hi Bouke,

Please see this migration guide:
https://swift.org/migration-guide/se-0107-migrate.html

It explains a few things that are not self-explanatory and includes some helper code for dealing with the socket API.

Given how your code is structured, I would start with a raw pointer, then replace all the ‘withMemoryRebound’ calls to ‘bindMemory’, and you should be fine:

var rawSockAddr = UnsafeRawPointer(CFDataGetBytePtr(data))

switch …
case …:
  let ipv4 = rawSockAddr.bindMemory(to: sockaddr_in.self, capacity: 1)

You might also be able to avoid CFData and use Swift’s Data directly, but any Swifty API you use will encourage you to restructure your code so that pointer access is confined to a closure, like Data.withUnsafeBytes, or UnsafePointer.withMemoryRebound(to:capacity:). You should never return the pointer argument from these closure. The closure taking APIs are designed to keep your data alive while you access it and make sure you’re not mixing pointers of different types to the same memory.

-Andy

On 12 sep. 2016, at 21:37, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Sorry, missed the first line when copying:

let generic = unsafeBitCast(CFDataGetBytePtr(data), to: sockaddr.self)
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:35, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Ah the missing part of the puzzle appears to be unsafeBitCast(:to:), so the Swift version becomes this:

switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
    //...
case sa_family_t(AF_INET6):
    let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
    //...
default:
    //...
}


Bouke

On 12 sep. 2016, at 21:25, Bouke Haarsma <bouke@haarsma.eu <mailto:bouke@haarsma.eu>> wrote:

Hi all,

Inside my CFSocketCallBack a pointer to a sockaddr is provided wrapped in a CFData structure. The sockaddr could be either a sockaddr_in (IPv4) or sockaddr_in6 (IPv6) struct. In order to discover which struct you’re dealing with, the attribute sa_family can be inspected. In C this would look something like this:

struct sockaddr_storage sa;

switch (((sockaddr*)&sa)->sa_family)
{
    case AF_INET:
        inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
        break;
    case AF_INET6:
        inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
        break;
}
(from: http://stackoverflow.com/a/13167913)

Wheras to do this in Swift 3, the only way I found thus far is this:

var generic = CFDataGetBytePtr(data).withMemoryRebound(to: sockaddr.self, capacity: 1) {
    return $0.pointee
}
switch generic.sa_family {
case sa_family_t(AF_INET):
    let ipv4 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
case sa_family_t(AF_INET6):
    let ipv6 = withUnsafePointer(to: &generic) {
        $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
            $0.pointee
        }
    }
    //...
default:
    //…
}

This looks very ugly with all the closures and ``withMemoryRebound``. Is there a better way to do this? I think there should be something more “Swifty”.


Bouke

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Quinn “The Eskimo!”) #9

Hey, it’s call /BSD/ Sockets for a reason (-:

Honestly, I don’t think there’s a good way to maintain the simplicity of this abstraction without `ss_len`. Special casing each address format isn’t viable because some addresses are of variable length (I’m looking at you `sockaddr_un`!).

You’re going to have to carry around a `sockaddr_storage` /and/ a `socklen_t`, and if you’re doing that it’d probably be best to build your own abstraction. When I originally wrote these helpers I was doing so in the context of a `QSocket` class, which had a `QSocket.Address` struct that wrapped `sockaddr_storage`, and that abstraction would have been able to cope with this.

Share and Enjoy

···

On 21 Sep 2016, at 06:45, Bouke Haarsma via swift-users <swift-users@swift.org> wrote:

This property is not available on Linux …

--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware