Or, can you manually add an Endpoint to the Bonjour service that will make this work?
Post
Replies
Boosts
Views
Activity
Thank you for your response, Quinn. The short answer is that it's a user requirement for my app. The longer answer is that very few enterprises use IPv6 addressing, some explicitly block it from their routing tables, and if someone elects to manually enter an IP address of a known system in their environment to connect to and it's in IPv4 format, when my app connects to it and reports success - it should be able to report that it connected to the same address that was entered. Instead, I am forced to use the remoteEndPoint which is only reported as an IPv6 address. It's confusing for the user. A better question is why is there an option to force IPv4 and it doesn't work but rather breaks things? I can live with this quirk, but it somewhat defeats the purpose of the flexibility advertised by the Network Framework.
I'm more interested in solving why I can connect across SSID / VLAN using Bonjour, but not using a direct IP Address and Port. I would be grateful if you please help me solve this part of my issue. I'm using wired Ethernet and not Wi-Fi - does that change your answer at all or does P2P only work for Bonjour? Your footnote says this is something you are able to discuss here. Should I assume that means you are not?
If not, is there another framework you would recommend to connect P2P to a direct IP Address? Thanks!
After posting this, I did some more testing and found that simply removing where I forced IPv4 at the bottom of the code in the previous post solved this issue..... partially. I can now connect across a network broadcast domain boundary, but I still cannot manually input an IP Address. I have to use Bonjour to cross the boundary. I can manually input the IP Address and connect when within the broadcast domain.
I would ideally like to limit everything to IPv4 addressing in my app, but given that that doesn't seem to work and connecting across SSID / VLAN / broadcast / multicast boundaries is more important, how can I either still limit to IPv4 or how can I connect directly to a given IP Address across SSID / VLAN boundaries with the Network Framework?
Here is my code. First up are the Constructors to my Connection object, including the one that takes an IP Address as a String:
// Bonjour Endpoint Constructor
public init( endpoint: NWEndpoint, key: Data ) async throws {
let parameters = NWParameters( authenticatingWithKey: key )
self.connection = NWConnection( to: endpoint, using: parameters )
( data, dataContinuation ) = AsyncThrowingStream.makeStream()
try await connect()
}
// Explicit IP Address Constructor
public init( ipAddress: String, key: Data ) async throws {
let parameters = NWParameters( authenticatingWithKey: key )
let host: NWEndpoint.Host = NWEndpoint.Host( ipAddress )
let port: NWEndpoint.Port = advertisedPort // Fixed
self.connection = NWConnection( host: host, port: port, using: parameters )
( data, dataContinuation ) = AsyncThrowingStream.makeStream()
try await connect()
}
// Server Constructor
public init( connection: NWConnection ) async throws {
self.connection = connection
( data, dataContinuation ) = AsyncThrowingStream.makeStream()
try await connect()
}
And here is my Connection.connect():
public func connect() async throws {
try await withCheckedThrowingContinuation { continuation in
connection.stateUpdateHandler = { [weak connection] state in
switch state {
case .ready:
connection?.stateUpdateHandler = { state in
switch state {
case .failed( let error ):
dataContinuation.finish( throwing: error )
break
case .cancelled:
dataContinuation.finish()
break
default:
break
}
}
continuation.resume()
break
case .preparing:
break
case .waiting( let waitingError ):
break
case .failed( let error ):
let out = "Failed with Error: \( error.localizedDescription )"
Task {
await AppDelegate.log( out )
}
continuation.resume( throwing: error )
break
default:
let out = "Other Connection Status: \( state )"
Task {
await AppDelegate.log( out )
}
print( out )
break
}
}
connection.start( queue: .main )
}
Self.receiveNextMessage( connection: connection, continuation: dataContinuation )
}
I can get as far as ".preparing" the connection with the manually-entered IP Address. Then, it hangs.
Did setting includePeerToPeer solve your issues? I have the same issues you describe above, but I am setting includePeerToPeer=true.
The only real difference is that I'm using TLS with a key. This works with simulated or real devices on the same broadcast domain, e.g. same VLAN, same SSID, or same multicast domain. I can see the advertisement on a different SSID or VLAN, but I cannot connect. I also have a function where you can specify an IP Address. Since I force advertising on a specific port and can start a connection with Bonjour locating the service, I am at a loss why I can't connect across the broadcast domain. Here is how I'm setting my NWParameters:
extension NWParameters {
convenience init( authenticatingWithKey key: Data ) {
let tlsOptions = NWProtocolTLS.Options()
let symmetricKey = SymmetricKey( data: key )
let code = HMAC<SHA256>.authenticationCode( for: Data( "AppleConnect".utf8 ), using: symmetricKey ).withUnsafeBytes( DispatchData.init )
sec_protocol_options_add_pre_shared_key( tlsOptions.securityProtocolOptions, code as dispatch_data_t, Data( "WindowProjectionTest".utf8 ).withUnsafeBytes( DispatchData.init ) as dispatch_data_t )
sec_protocol_options_append_tls_ciphersuite( tlsOptions.securityProtocolOptions, tls_ciphersuite_t( rawValue: numericCast( TLS_PSK_WITH_AES_128_GCM_SHA256 ) )! )
// TLS PSK requires TLSv1.2 on Apple platforms - See https://developer.apple.com/forums/thread/688508.
sec_protocol_options_set_max_tls_protocol_version( tlsOptions.securityProtocolOptions, .TLSv12 )
sec_protocol_options_set_min_tls_protocol_version( tlsOptions.securityProtocolOptions, .TLSv12 ) // Requires Second Attempt
self.init( tls: tlsOptions )
defaultProtocolStack.applicationProtocols.insert( NWProtocolFramer.Options( definition: .init( implementation: ConnectProtocol.self ) ), at: 0 )
// Set Connection Configuration
acceptLocalOnly = false
allowLocalEndpointReuse = true
includePeerToPeer = true
prohibitedInterfaceTypes = [ NWInterface.InterfaceType.loopback ]
// Force IPv4
if let isOption = defaultProtocolStack.internetProtocol as? NWProtocolIP.Options {
isOption.version = .v4
}
}
}