Pointer conversions between different sockaddr types


(Martin R) #1

I am trying to figure out how to work correctly with the new UnsafeRawPointer API (from https://github.com/apple/swift-evolution/blob/master/proposals/0107-unsaferawpointer.md ).

Here my concrete example: The connect() system call takes a (const struct sockaddr *address) parameter, but one actually passes the address of a struct sockaddr_in or sockaddr_in6 (or ...).

In Swift 2.2 this could be done as

    var addr = sockaddr_in() // or sockaddr_in6()
    // fill addr fields ...
    let sock = socket(PF_INET, SOCK_STREAM, 0)

    let result = withUnsafePointer(&addr) {
        connect(sock, UnsafePointer($0), socklen_t(strideofValue(addr)))
    }

With the latest Swift from Xcode 8 beta 6, unsafe pointers cannot be simply initialized from a different kind of unsafe pointer anymore. I came up with two different solutions:

    let result = withUnsafePointer(to: &addr) {
        connect(sock,
                UnsafeRawPointer($0).assumingMemoryBound(to: sockaddr.self),
                socklen_t(MemoryLayout<sockaddr_in>.stride))
    }

or

    let result = withUnsafePointer(to: &addr) {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
            connect(sock, $0, socklen_t(MemoryLayout<sockaddr_in>.stride))
        }
    }

which both compile and run correctly.

My questions are:
- Are both solutions correct, should one be preferred, or are both wrong?
- Can the same be achieved simpler?

Thanks,
Martin


(Quinn “The Eskimo!”) #2

- Are both solutions correct, should one be preferred, or are both wrong?

Your `withMemoryRebound` solution is correct.

- Can the same be achieved simpler?

Not without introducing a layer of abstraction.

In my case I introduced an abstract `Address` type (basically a wrapper around `sockaddr_storage`) and then added a method to that object which calls a closure with the right parameters (actually, multiple such methods, depending on whether I’m calling something like `connect` which takes an address, or `getpeername`, which returns one). This approach concentrates all the ugly in one place, making the rest of my BSD Sockets code much cleaner.

Share and Enjoy

···

On 17 Aug 2016, at 18:55, Martin R via swift-users <swift-users@swift.org> wrote:
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware


(Andrew Trick) #3

- Are both solutions correct, should one be preferred, or are both wrong?

Your `withMemoryRebound` solution is correct.

Absolutely, withMemoryRebound is always safe. You can use it whenever you just need to reinterpret memory at a call site and know the number of values stored that memory location. In this case it’s “easy" because you’re dealing a single sockaddr_in.

The UnsafeRawPointer proposal is the definitive reference
https://github.com/apple/swift-evolution/blob/master/proposals/0107-unsaferawpointer.md

But this migration guide is more approachable… it’s still WIP:
https://gist.github.com/atrick/0283ae0e284610fd21ad6ed3f454a585

- Can the same be achieved simpler?

Not without introducing a layer of abstraction.

In my case I introduced an abstract `Address` type (basically a wrapper around `sockaddr_storage`) and then added a method to that object which calls a closure with the right parameters (actually, multiple such methods, depending on whether I’m calling something like `connect` which takes an address, or `getpeername`, which returns one). This approach concentrates all the ugly in one place, making the rest of my BSD Sockets code much cleaner.

This is an annoying UpdatePointer migration case because it falls under the category of misbehaving C APIs that we deliberately don't want to encourage in Swift.

The only good answer is to provide a Swift wrapper on top of the socket API as Quinn has done. It would be nice to post that code at some point so users of the socket API can copy-paste into their project.

-Andy

···

On Aug 18, 2016, at 12:28 AM, Quinn The Eskimo! via swift-users <swift-users@swift.org> wrote:
On 17 Aug 2016, at 18:55, Martin R via swift-users <swift-users@swift.org> wrote:


(Quinn “The Eskimo!”) #4

I’ve been revisiting this issue recently and decided to tidy up my code enough to share with others. It’s pasted in below. Bon apétit!

Share and Enjoy

···

On 18 Aug 2016, at 08:28, Quinn The Eskimo! via swift-users <swift-users@swift.org> wrote:

In my case I introduced an abstract `Address` type (basically a wrapper around `sockaddr_storage`) and then added a method to that object which calls a closure with the right parameters (actually, multiple such methods, depending on whether I’m calling something like `connect` which takes an address, or `getpeername`, which returns one). This approach concentrates all the ugly in one place, making the rest of my BSD Sockets code much cleaner.

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

---------------------------------------------------------------------------
import Darwin

extension sockaddr_storage {

    /// Calls a closure with traditional BSD Sockets address parameters.
    ///
    /// This is used to call BSD Sockets routines like `connect`, which accept their
    /// address as an `sa` and `saLen` pair. For example:
    ///
    /// let ss: sockaddr_storage = …
    /// let connectResult = ss.withSockAddr { (sa, saLen) in
    /// connect(fd, sa, saLen)
    /// }
    ///
    /// - parameter body: A closure to call with `self` referenced appropriately for calling
    /// BSD Sockets APIs that take an address.
    ///
    /// - throws: Any error thrown by `body`.
    ///
    /// - returns: Any result returned by `body`.
    
    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
        // Get a typed unsafe pointer to `ss`.
        return try withUnsafePointer(to: &ss) {
            // Temporarily view that as `sockaddr` while we call `body`.
            try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                try body($0, socklen_t(self.ss_len))
            }
        }
    }

    /// Calls a closure such that it can return an address based on traditional BSD Sockets parameters.
    ///
    /// This is used to call BSD Sockets routines like `accept`, which return a value (the file
    /// descriptor) and an address via memory pointed to by `sa` and `saLen` parameters. For example:
    ///
    /// let (acceptResult, peerAddr) = sockaddr_storage.fromSockAddr { (_ sa: UnsafeMutablePointer<sockaddr>, _ saLen: inout socklen_t) in
    /// return accept(fd, sa, &saLen)
    /// }
    ///
    /// - parameter body: A closure to call with parameters appropriate for calling BSD Sockets APIs
    /// that return an address.
    ///
    /// - throws: Any error thrown by `body`.
    ///
    /// - returns: A tuple consistent of the result returned by `body` and an address set up by
    /// `body` via its `sa` and `saLen` parameters.

    static func fromSockAddr<ReturnType>(_ body: (_ sa: UnsafeMutablePointer<sockaddr>, _ saLen: inout socklen_t) throws -> ReturnType) rethrows -> (ReturnType, sockaddr_storage) {
        // We need a mutable `sockaddr_storage` so that we can pass it to `withUnsafePointer(to:_:)`.
        var ss = sockaddr_storage()
        // Similarly, we need a mutable copy of our length for the benefit of `saLen`.
        var saLen = socklen_t(MemoryLayout<sockaddr_storage>.size)
        // Get a typed unsafe pointer to `ss`.
        let result = try withUnsafePointer(to: &ss) {
            // Temporarily view that as `sockaddr` while we call `body`.
            try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                try body($0, &saLen)
            }
        }
        return (result, ss)
    }

    /// Calls a closure with an address parameter of a user-specified type.
    ///
    /// This makes it easy to access the fields of an address as the appropriate type. For example:
    ///
    /// let sin: sockaddr_storage = … initialise with an AF_INET address …
    /// sin.withSockAddrType { (sin: inout sockaddr_in) in
    /// print(sin.sin_len)
    /// print(UInt16(bigEndian: sin.sin_port))
    /// }
    ///
    /// In this case the closure returns void, but there may be other circumstances where it's useful
    /// to have a return type.
    ///
    /// - note: `body` takes an inout parameter for the sake of folks who need to take
    /// a pointer to elements of that parameter. We ignore any changes that the `body`
    /// might make to this value. Without this affordance, the following code would not
    /// work:
    ///
    /// let sus: sockaddr_storage = … initialise with an AF_UNIX address …
    /// sus.withSockAddrType { (sun: inout sockaddr_un) in
    /// print(sun.sun_len)
    /// print(String(cString: &sun.sun_path.0))
    /// }
    ///
    /// - parameter body: A closure to call with `self` referenced via an arbitrary type.
    /// Careful with that axe, Eugene.
    ///
    /// - throws: Any error thrown by `body`.
    ///
    /// - returns: Any result returned by `body`.
    ///
    /// - precondition: `AddrType` must not be larger than `sockaddr_storage`.

    func withSockAddrType<AddrType, ReturnType>(_ body: (_ sax: inout AddrType) throws -> ReturnType) rethrows -> ReturnType {
        precondition(MemoryLayout<AddrType>.size <= MemoryLayout<sockaddr_storage>.size)
        // We need to create a mutable copy of `self` so that we can pass it to `withUnsafePointer(to:_:)`.
        var ss = self
        // Get a typed unsafe pointer to `ss`.
        return try withUnsafeMutablePointer(to: &ss) {
            // Temporarily view that as `AddrType` while we call `body`.
            try $0.withMemoryRebound(to: AddrType.self, capacity: 1) {
                try body(&$0.pointee)
            }
        }
    }

    /// Calls a closure such that it can return an address via a user-specified type.
    ///
    /// This is useful if you want to create an address from a specific sockaddr_xxx
    /// type that you initialise piecemeal. For example:
    ///
    /// let (_, sin) = sockaddr_storage.fromSockAddr { (sin: inout sockaddr_in) in
    /// sin.sin_family = sa_family_t(AF_INET)
    /// sin.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
    /// sin.sin_port = (12345 as in_port_t).bigEndian
    /// }
    ///
    /// In this case the closure returns void, but there may be other circumstances where it's useful
    /// to have a return type.
    ///
    /// - parameter body: A closure to call with parameters appropriate for returning an address.
    ///
    /// - throws: Any error thrown by `body`.
    ///
    /// - returns: A tuple consistent of the result returned by `body` and an address set
    /// up by `body` via the `sax` inout parameter.
    ///
    /// - precondition: `AddrType` must not be larger than `sockaddr_storage`.

    static func fromSockAddr<AddrType, ReturnType>(_ body: (_ sax: inout AddrType) throws -> ReturnType) rethrows -> (ReturnType, sockaddr_storage) {
        precondition(MemoryLayout<AddrType>.size <= MemoryLayout<sockaddr_storage>.size)
        // We need a mutable `sockaddr_storage` so that we can pass it to `withUnsafePointer(to:_:)`.
        var ss = sockaddr_storage()
        // Get a typed unsafe pointer to `ss`.
        let result = try withUnsafePointer(to: &ss) {
            // Temporarily view that as `AddrType` while we call `body`.
            try $0.withMemoryRebound(to: AddrType.self, capacity: 1) {
                try body(&$0.pointee)
            }
        }
        return (result, ss)
    }
}
---------------------------------------------------------------------------


(Michael Gottesman) #5

- Are both solutions correct, should one be preferred, or are both wrong?

Your `withMemoryRebound` solution is correct.

Absolutely, withMemoryRebound is always safe. You can use it whenever you just need to reinterpret memory at a call site and know the number of values stored that memory location. In this case it’s “easy" because you’re dealing a single sockaddr_in.

The UnsafeRawPointer proposal is the definitive reference
https://github.com/apple/swift-evolution/blob/master/proposals/0107-unsaferawpointer.md

But this migration guide is more approachable… it’s still WIP:
https://gist.github.com/atrick/0283ae0e284610fd21ad6ed3f454a585

- Can the same be achieved simpler?

Not without introducing a layer of abstraction.

In my case I introduced an abstract `Address` type (basically a wrapper around `sockaddr_storage`) and then added a method to that object which calls a closure with the right parameters (actually, multiple such methods, depending on whether I’m calling something like `connect` which takes an address, or `getpeername`, which returns one). This approach concentrates all the ugly in one place, making the rest of my BSD Sockets code much cleaner.

This is an annoying UpdatePointer migration case because it falls under the category of misbehaving C APIs that we deliberately don't want to encourage in Swift.

The only good answer is to provide a Swift wrapper on top of the socket API as Quinn has done. It would be nice to post that code at some point so users of the socket API can copy-paste into their project.

I wonder if we could provide it via an overlay or if it is a more general thing that could use a generic/protocol? Just a random thought.

···

On Aug 19, 2016, at 1:58 PM, Andrew Trick via swift-users <swift-users@swift.org> wrote:

On Aug 18, 2016, at 12:28 AM, Quinn The Eskimo! via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
On 17 Aug 2016, at 18:55, Martin R via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

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


#6

Hi Quinn, and others,

I found that this code is still featured on swift.org (https://swift.org/migration-guide/se-0107-migrate.html), but it doesn't compile anymore. There's a fix-it for adding @escaping, but also compile errors due to invalid casts. Can someone have a look at fixing this code?

--Bouke

···

On 2016-08-22 16:31:58 +0000, Quinn \"The Eskimo!\" via swift-users said:

On 18 Aug 2016, at 08:28, Quinn The Eskimo! via swift-users > <swift-users@swift.org> wrote:

In my case I introduced an abstract `Address` type (basically a wrapper around `sockaddr_storage`) and then added a method to that object which calls a closure with the right parameters (actually, multiple such methods, depending on whether I’m calling something like `connect` which takes an address, or `getpeername`, which returns one). This approach concentrates all the ugly in one place, making the rest of my BSD Sockets code much cleaner.

I’ve been revisiting this issue recently and decided to tidy up my code enough to share with others. It’s pasted in below. Bon apétit!

Share and Enjoy


#7

Hi all (again),

It seems the fix is to (only) replace withUnsafePointer with withUnsafeMutablePointer twice. No need to apply the @escaping fixit even.

···

--
Bouke

On 2017-03-21 20:52:36 +0000, Bouke Haarsma via swift-users said:

Hi Quinn, and others,

I found that this code is still featured on swift.org (https://swift.org/migration-guide/se-0107-migrate.html), but it doesn't compile anymore. There's a fix-it for adding @escaping, but also compile errors due to invalid casts. Can someone have a look at fixing this code?

--Bouke

On 2016-08-22 16:31:58 +0000, Quinn \"The Eskimo!\" via swift-users said:

On 18 Aug 2016, at 08:28, Quinn The Eskimo! via swift-users >> <swift-users@swift.org> wrote:

In my case I introduced an abstract `Address` type (basically a wrapper around `sockaddr_storage`) and then added a method to that object which calls a closure with the right parameters (actually, multiple such methods, depending on whether I’m calling something like `connect` which takes an address, or `getpeername`, which returns one). This approach concentrates all the ugly in one place, making the rest of my BSD Sockets code much cleaner.

I’ve been revisiting this issue recently and decided to tidy up my code enough to share with others. It’s pasted in below. Bon apétit!

Share and Enjoy

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