EPERM when connecting to Socket with Posix

I want to connect my iOS App with a MQTT Server via a Kotlin Multiplatform XCFramework.

This Framework uses the library KMQTT to connect to our MQTT Server. As you can see Here KMQTT uses the default POSIX commands to connect to a socket. This setup works great on Android, not so much for iOS:

  1. When connecting to a IPv6 Address iOS devices get the POSIX Error 47, this was "fixed" by only using IPv4
  2. more importantly: On ~50% of devices, when connecting to the socket they get the POSIX Error 1 (EPERM). The devices are iPhone 13, 14 Pro and a 15 and they all use either iOS 17 or iOS 18. When trying to open the ip via Safari they can connect. This problem seems to come from the Provider, as when i open a Hotspot from a device that doesn't work, all connected iOS Devices also don't work and when another device that works opens a hotspot and the not-working device connects to it, this device works.

Do you guys have any idea, why this error is thrown?

Error 47 is EAFNOSUPPORT. That’s weird, because iOS most definitely supports IPv6. Consider this code:

func test() {
    Thread.detachNewThread {
        do {
            let fd = try FileDescriptor.socket(AF_INET6, SOCK_STREAM, 0)
            defer { try! fd.close() }
            try fd.connect("fe80::7d:e738:7ade:d875%en0", 12345)
            _ = try fd.write(data: .init("Hello Cruel World! \(Date.now)\r\n".utf8))
        } catch {
            print(error)
        }
    }
}

where:

  • I’m using the BSD Sockets abstraction referenced by Extra-ordinary Networking.

  • fe80::7d:e738:7ade:d875%en0 is the link-local IPv6 address of my Mac.

I put this into an iOS app and ran it on a device. I then ran nc on my Mac to listen for the connection. When I tap the Test button, I see this:

% nc -6 -l 12345
Hello Cruel World! 2024-10-17 08:53:18 +0000

IMPORTANT The first time around the code failed due to local network privacy. Once I authorised the app to use the local network, it worked. See Local Network Privacy FAQ for more about that.

To debug your app you’ll need to dig into your third-party library to see exactly how it’s calling our BSD Sockets API. Alternatively, you could escalate this via the support channel for the library and tools you’re using.


Regarding your EPERM issue, that’s a bit more mysterious. You wrote:

This problem seems to come from the Provider

What do you mean by “Provider” in this context?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for the reply.

After further testing (and only implementing the bare minimum of the KMQTT library) i now only get the EPERM issue when connecting via IPv6 and i don't get the EAFNOTSUPPORT anymore. The EAFNOTSUPPORT could just have been a side effect.

I also dug deeper in the KMQTT Library and found the full POSIX usage here. It looks like it only uses sizeof(sockaddr_in), so only the size of the IPv4 address without considering IPv6, but somehow this still works on Android.

I've tried to reproduce the issue with the following swift playground code and i also keep getting EPERM unless i use the IPv4 Address & size.

import Foundation


Thread.detachNewThread {
    let socket = socket(AF_INET, SOCK_STREAM, 0)
    guard socket != -1 else {
        print("error creating Socket: \(errno)")
        return
    }
    var hints = addrinfo(
        ai_flags: AI_PASSIVE,
        ai_family: AF_UNSPEC,       // Either IPv4 or IPv6
        ai_socktype: SOCK_STREAM,   // TCP
        ai_protocol: IPPROTO_TCP,
        ai_addrlen: 0,
        ai_canonname: nil,
        ai_addr: nil,
        ai_next: nil)
    
    var servinfo: UnsafeMutablePointer<addrinfo>?
    var name = "test.mosquitto.org".toPointer()
    var port = "1883".toPointer()
    let addrInfoResult = getaddrinfo(
        name,
        port,
        nil,
        &servinfo)
    
    if addrInfoResult != 0 {
        print("Error getting address: \(errno)")
        return
    }
    
    let ipv4_size = UInt32(MemoryLayout<sockaddr_in>.size)
    let ipv6_size = UInt32(MemoryLayout<sockaddr_in6>.size)
    let dynamic_size = servinfo!.pointee.ai_addrlen
    
    let connectResult = connect(socket, servinfo?.pointee.ai_addr, ipv6_size)
    
    if connectResult == -1 {
        print("Error connecting: \(errno)")
        return
    }
}

extension String {
    func toPointer() -> UnsafePointer<Int8>? {
        guard let data = self.data(using: String.Encoding.utf8) else { return nil }

        let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: data.count)
        let stream = OutputStream(toBuffer: buffer, capacity: data.count)

        stream.open()
        data.withUnsafeBytes({ (p: UnsafePointer<Int8>) -> Void in
          stream.write(p, maxLength: data.count)
        })

        stream.close()

        return UnsafePointer<Int8>(buffer)
      }
}
EPERM when connecting to Socket with Posix
 
 
Q