5 Replies
      Latest reply: Sep 21, 2016 2:39 AM by eskimo RSS
      luis4837 Level 1 Level 1 (0 points)

        Getting an error trying to create UnsafeMutablePointer<UInt8> for the CFDataCreate below.

         

         

        let serverIP:String = addressInfo[0]

        let serverIPCString = serverIP.cString(using: String.Encoding.ascii)

        let binaryAddress:UnsafeMutablePointer<in_addr_t> = UnsafeMutablePointer<in_addr_t>.allocate(capacity: 1)

        inet_pton(AF_INET, serverIPCString!, binaryAddress)

        serverSocketAddrPointer.pointee.sin_len = __uint8_t(MemoryLayout<sockaddr_in>.size)

        serverSocketAddrPointer.pointee.sin_family = sa_family_t(AF_INET)

        serverSocketAddrPointer.pointee.sin_port = in_port_t(serverPort.bigEndian)

        serverSocketAddrPointer.pointee.sin_zero = (0,0,0,0,0,0,0,0)

        serverSocketAddrPointer.pointee.sin_addr = in_addr(s_addr: binaryAddress.pointee)

         

        let connectServerAddrPointer:UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>(serverSocketAddrPointer)

        error: ‘init’ is unavailable: use ‘withMemoryRebound(to:capacity:_)’ to temporarily view memory as another layout-compatible type.

         

        let connectAddr = CFDataCreate(kCFAllocatorDefault, connectServerAddrPointer, MemoryLayout<sockaddr_in>.size)

         

        How can I correct this for Swift3?

        • Re: swift3 - Error creating UnsafeMutablePointer<UInt8>
          eskimo Apple Staff Apple Staff (7,530 points)

          What are you actually trying to do with this code?  Specifically, what data are you starting with?  And what data are you trying to end up with?

          There’s an obvious solution here (rebind the address to a new type, as suggested by the fix it) but there’s a more likely to be a better solution (once that’s both easier to write and does not hard code IPv4).

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: swift3 - Error creating UnsafeMutablePointer<UInt8>
              luis4837 Level 1 Level 1 (0 points)

              I am trying to initiate a UDP connection to network devices for monitoring. For this connection, user input of an IP or host name would be passed thru my CFHost function for DNS resolution returning an array of address(es). The first IPv4 address in that array would then be passed to udpConnection below.

               

               

              Original code that worked on Swift2.3:

              var isConnected:Bool = false

              var socketRef:CFSocketRef?

              var serverPort = UInt16(161)

              var serverSocketAddrPointer:UnsafeMutablePointer<sockaddr_in> = UnsafeMutablePointer<sockaddr_in>.alloc(1)

              var connectError = ""

               

              func udpConnection(addressInfo: [String], timeout: Double) -> (isConnected: Bool, error: String) {

                  if false == isConnected {

                      socketRef = CFSocketCreate(kCFAllocatorDefault, AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, nil, nil)

                      if (socketRef != nil) {

                          let serverIP:String = addressInfo[0]

                          let serverIPCString = serverIP.cStringUsingEncoding(NSASCIIStringEncoding)

                          let binaryAddress:UnsafeMutablePointer<in_addr_t> = UnsafeMutablePointer<in_addr_t>.alloc(1)

                          inet_pton(AF_INET, serverIPCString!, binaryAddress)

                          serverSocketAddrPointer.memory.sin_len = __uint8_t(sizeof(sockaddr_in))

                          serverSocketAddrPointer.memory.sin_family = sa_family_t(AF_INET)

                          serverSocketAddrPointer.memory.sin_port = in_port_t(serverPort.bigEndian)

                          serverSocketAddrPointer.memory.sin_zero = (0,0,0,0,0,0,0,0)

                          serverSocketAddrPointer.memory.sin_addr = in_addr(s_addr: binaryAddress.memory)

                          let connectServerAddrPointer:UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>(serverSocketAddrPointer)

                          let connectAddr = CFDataCreate(kCFAllocatorDefault, connectServerAddrPointer, sizeof(sockaddr_in))!

                          let connectionResult = CFSocketConnectToAddress(socketRef, connectAddr, CFTimeInterval(timeout))

                          if connectionResult == CFSocketError.Success {

                              isConnected = true

                              connectError = "NoError"

                          } else if connectionResult == CFSocketError.Error {

                              connectError = "An Error has occurred"

                          } else if connectionResult == CFSocketError.Timeout {

                              connectError = "Connection Attempt Timed Out"

                          } else {

                              connectError = "Unable to connect socket"

                          }

                      }

                  } else {

                      if (socketRef != nil) {

                          CFSocketIsValid(socketRef)

                      }

                  }

                  return (isConnected, connectError)

              }

                • Re: swift3 - Error creating UnsafeMutablePointer<UInt8>
                  eskimo Apple Staff Apple Staff (7,530 points)

                  CFHost gives you back an array of CFDatas, each containing a sockaddrCFSocketConnectToAddress takes a CFData containing a sockaddr.  Gluing these two together should be relatively straightforward, and (mostly) does not require you to grovel around inside the sockaddr_xxx types.

                  Consider this.

                  let host: CFHost = … a resolved CFHost …
                  let a1: Unmanaged<CFArray>? = CFHostGetAddressing(host, nil)
                  guard let a2: Unmanaged<CFArray> = a1 else {
                      … handle error …
                  }
                  let a3: CFArray = a2.takeUnretainedValue()
                  let addresses: [CFData] = a3 as! [CFData]
                  for address in addresses {
                      let family = CFDataGetBytePtr(address)!.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                          return $0.pointee.sa_family
                      }
                      guard let s = CFSocketCreate(nil, Int32(family), SOCK_DGRAM, 0, 0, nil, nil) else {
                          … handle error …
                      }
                  
                      let err = CFSocketConnectToAddress(s, address, 0.0)
                      switch err {
                          case .success:
                              … handle success …
                          case .error:
                              … handle error …
                          case .timeout:
                              … handle timeout …
                      }
                  }
                  

                  There’s a couple of weird things here:

                  • Lines 2 through 7 are complex because CFHostGetAddressing is quite unpleasant to use from Swift.  I recommend you file a bug requesting that it be made more Swift friendly.  For the moment, you could write your own wrapper function that abstracts this away.

                  • Lines 9 through 11 is the only place where I have to grovel around within the CFData, to extract the sa_family value.

                  Finally, this code will support IPv6 out of the box; if you don’t want that, you can filter the addresses based on the ‘family’ value.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"

                    • Re: swift3 - Error creating UnsafeMutablePointer<UInt8>
                      luis4837 Level 1 Level 1 (0 points)

                      How would I include that I want to connect via UDP port 161?

                      var serverPort = UInt16(161)

                      in_port_t(serverPort.bigEndian)

                        • Re: swift3 - Error creating UnsafeMutablePointer<UInt8>
                          eskimo Apple Staff Apple Staff (7,530 points)

                          Ah, yeah, that’s a pain.  With the standard BSD Sockets resolver, getaddrinfo, you can specify a port via its ‘service name’ parameter.  Unfortunately CFHost has no such facility.  Without that, you have to write the annoying address-family-specific code that I was trying to avoid.  To wit:

                          let address: CFData = … from the previous example …
                          let addressWithPort = CFDataCreateMutableCopy(nil, 0, address)!
                          CFDataGetMutableBytePtr(addressWithPort)!.withMemoryRebound(to: sockaddr.self, capacity: 1) { sa in
                              switch Int32(sa.pointee.sa_family) {
                                  case AF_INET:
                                      sa.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { sin in
                                          sin.pointee.sin_port = (161 as UInt16).bigEndian
                                      }
                                  case AF_INET6:
                                      sa.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) { sin6 in
                                          sin6.pointee.sin6_port = (161 as UInt16).bigEndian
                                      }
                                  default:
                                      fatalError()
                              }
                          }
                          

                          Honestly, in your shoes I might just switch to getaddrinfo.  If you do make that switch, however, remember that getaddrinfo is a synchronous blocking networking call, and thus can’t be made on the main thread.

                          Share and Enjoy

                          Quinn “The Eskimo!”
                          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                          let myEmail = "eskimo" + "1" + "@apple.com"