Post

Replies

Boosts

Views

Activity

NWBrowser + NWListener + NWConnection
I am seeking assistance with how to properly handle / save / reuse NWConnections when it comes to the NWBrowser vs NWListener. Let me give some context surrounding why I am trying to do what I am. I am building an iOS app that has peer to peer functionality. The design is for a user (for our example the user is Bob) to have N number of devices that have my app installed on it. All these devices are near each other or on the same wifi network. As such I want all the devices to be able to discover each other and automatically connect to each other. For example if Bob had three devices (A, B, C) then A discovers B and C and has a connection to each, B discovers B and C and has a connection to each and finally C discovers A and B and has a connection to each. In the app there is a concept of a leader and a follower. A leader device issues commands to the follower devices. A follower device just waits for commands. For our example device A is the leader and devices B and C are followers. Any follower device can opt to become a leader. So if Bob taps the “become leader” button on device B - device B sends out a message to all the devices it’s connected to telling them it is becoming the new leader. Device B doesn’t need to do anything but device A needs to set itself as a follower. This detail is to show my need to have everyone connected to everyone. Please note that I am using .includePeerToPeer = true in my NWParameters. I am using http/3 and QUIC. I am using P12 identity for TLS1.3. I am successfully able to verify certs in sec_protocal_options_set_verify_block. I am able to establish connections - both from the NWBrowser and from NWListener. My issue is that it’s flaky. I found that I have to put a 3 second delay prior to establishing a connection to a peer found by the NWBrowser. I also opted to not save the incoming connection from NWListener. I only save the connection I created from the peer I found in NWBrowser. For this example there is Device X and Device Y. Device X discovers device Y and connects to it and saves the connection. Device Y discovers device X and connects to it and saves the connection. When things work they work great - I am able to send messages back and forth. Device X uses the saved connection to send a message to device Y and device Y uses the saved connection to send a message to device X. Now here come the questions. Do I save the connection I create from the peer I discovered from the NWBrowser? Do I save the connection I get from my NWListener via newConnectionHandler? And when I save a connection (be it from NWBrowser or NWListener) am I able to reuse it to send data over (ie “i am the new leader command”)? When my NWBrowser discovers a peer, should I be able to build a connection and connect to it immediately? I know if I save the connection I create from the peer I discover I am able to send messages with it. I know if I save the connection from NWListener - I am NOT able to send messages with it — but should I be able to? I have a deterministic algorithm for who makes a connection to who. Each device has an ID - it is a UUID I generate when the app loads - I store it in UserDefaults and the next time I try and fetch it so I’m not generating new UUIDs all the time. I set this deviceID as the name of the NWListener.Service I create. As a result the peer a NWBrowser discovers has the deviceID set as its name. Due to this the NWBrowser is able to determine if it should try and connect to the peer or if it should not because the discovered peer is going to try and connect to it. So the algorithm above would be great if I could save and use the connection from NWListener to send messages over.
25
0
346
3w
Using Network Framework + Bonjour + QUIC + TLS
Hello, I was able to use the TicTackToe code base and modify it such that I have a toggle at the top of the screen that allows me to start / stop the NWBrowser and NWListener. I have it setup so when the browser finds another device it attempts to connect to it. I support N devices / connections. I am able to use the NWParameters extension that is in the TickTackToe game that uses a passcode and TLS. I am able to send messages between devices just fine. Here is what I used extension NWParameters { // Create parameters for use in PeerConnection and PeerListener. convenience init(passcode: String) { // Customize TCP options to enable keepalives. let tcpOptions = NWProtocolTCP.Options() tcpOptions.enableKeepalive = true tcpOptions.keepaliveIdle = 2 // Create parameters with custom TLS and TCP options. self.init(tls: NWParameters.tlsOptions(passcode: passcode), tcp: tcpOptions) // Enable using a peer-to-peer link. self.includePeerToPeer = true } // Create TLS options using a passcode to derive a preshared key. private static func tlsOptions(passcode: String) -> NWProtocolTLS.Options { let tlsOptions = NWProtocolTLS.Options() let authenticationKey = SymmetricKey(data: passcode.data(using: .utf8)!) let authenticationCode = HMAC<SHA256>.authenticationCode(for: "HI".data(using: .utf8)!, using: authenticationKey) let authenticationDispatchData = authenticationCode.withUnsafeBytes { DispatchData(bytes: $0) } sec_protocol_options_add_pre_shared_key(tlsOptions.securityProtocolOptions, authenticationDispatchData as __DispatchData, stringToDispatchData("HI")! as __DispatchData) sec_protocol_options_append_tls_ciphersuite(tlsOptions.securityProtocolOptions, tls_ciphersuite_t(rawValue: TLS_PSK_WITH_AES_128_GCM_SHA256)!) return tlsOptions } // Create a utility function to encode strings as preshared key data. private static func stringToDispatchData(_ string: String) -> DispatchData? { guard let stringData = string.data(using: .utf8) else { return nil } let dispatchData = stringData.withUnsafeBytes { DispatchData(bytes: $0) } return dispatchData } } When I try to modify it to use QUIC and TLS 1.3 like so extension NWParameters { // Create parameters for use in PeerConnection and PeerListener. convenience init(psk: String) { self.init(quic: NWParameters.quicOptions(psk: psk)) self.includePeerToPeer = true } private static func quicOptions(psk: String) -> NWProtocolQUIC.Options { let quicOptions = NWProtocolQUIC.Options(alpn: ["h3"]) let authenticationKey = SymmetricKey(data: psk.data(using: .utf8)!) let authenticationCode = HMAC<SHA256>.authenticationCode(for: "hello".data(using: .utf8)!, using: authenticationKey) let authenticationDispatchData = authenticationCode.withUnsafeBytes { DispatchData(bytes: $0) } sec_protocol_options_set_min_tls_protocol_version(quicOptions.securityProtocolOptions, .TLSv13) sec_protocol_options_set_max_tls_protocol_version(quicOptions.securityProtocolOptions, .TLSv13) sec_protocol_options_add_pre_shared_key(quicOptions.securityProtocolOptions, authenticationDispatchData as __DispatchData, stringToDispatchData("hello")! as __DispatchData) sec_protocol_options_append_tls_ciphersuite(quicOptions.securityProtocolOptions, tls_ciphersuite_t(rawValue: TLS_AES_128_GCM_SHA256)!) sec_protocol_options_set_verify_block(quicOptions.securityProtocolOptions, { _, _, sec_protocol_verify_complete in sec_protocol_verify_complete(true) }, .main) return quicOptions } // Create a utility function to encode strings as preshared key data. private static func stringToDispatchData(_ string: String) -> DispatchData? { guard let stringData = string.data(using: .utf8) else { return nil } let dispatchData = stringData.withUnsafeBytes { DispatchData(bytes: $0) } return dispatchData } } I get the following errors in the console boringssl_session_handshake_incomplete(241) [C3:1][0x109d0c600] SSL library error boringssl_session_handshake_error_print(44) [C3:1][0x109d0c600] Error: 4459057536:error:100000ae:SSL routines:OPENSSL_internal:NO_CERTIFICATE_SET:/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/tls13_server.cc:882: boringssl_session_handshake_incomplete(241) [C4:1][0x109d0d200] SSL library error boringssl_session_handshake_error_print(44) [C4:1][0x109d0d200] Error: 4459057536:error:100000ae:SSL routines:OPENSSL_internal:NO_CERTIFICATE_SET:/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/tls13_server.cc:882: nw_endpoint_flow_failed_with_error [C3 fe80::1884:2662:90ca:b011%en0.65328 in_progress channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], scoped, ipv4, dns, uses wifi)] already failing, returning nw_endpoint_flow_failed_with_error [C4 192.168.0.98:65396 in_progress channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], scoped, ipv4, dns, uses wifi)] already failing, returning quic_crypto_connection_state_handler [C1:1] [2ae0263d7dc186c7-] TLS error -9858 (state failed) nw_connection_copy_connected_local_endpoint_block_invoke [C3] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection nw_connection_copy_connected_remote_endpoint_block_invoke [C3] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection nw_connection_copy_protocol_metadata_internal_block_invoke [C3] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection quic_crypto_connection_state_handler [C2:1] [84fdc1e910f59f0a-] TLS error -9858 (state failed) nw_connection_copy_connected_local_endpoint_block_invoke [C4] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection nw_connection_copy_connected_remote_endpoint_block_invoke [C4] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection nw_connection_copy_protocol_metadata_internal_block_invoke [C4] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection Am I missing some configuration? I noticed with the working code that uses TCP and TLS that there is an NWParameters initializer that accepts tls options and tcp option but there isnt one that accepts tls and quic. Thank you for any help :)
19
0
1.6k
Dec ’24