[swift-users] Pointer conversions between different sockaddr types

Quinn "The Eskimo!" eskimo1 at apple.com
Mon Aug 22 11:31:58 CDT 2016


On 18 Aug 2016, at 08:28, Quinn The Eskimo! via swift-users <swift-users at 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
--
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)
    }
}
---------------------------------------------------------------------------



More information about the swift-users mailing list