When using Network framework, is it possible to set NWProtocolTLS behave like TLS Server or Client? In CFNetwork there is a kCFStreamSSLIsServer key which I could not find the same thing in Network.
I currently try to implement something like STARTTLS, both client and server side, after connection and some message, the client will behave like a TLS SERVER, and the connection in server(NWListener) will behave like a TLS CLIENT.
That's why i need to set something like kCFStreamSSLIsServer
In Swift-NIO, this can be easily implemented by adding a NIOSSLClientHandler or NIOSSLServerHandler
Below it's what I got currently based on another post in community
// main.swift
import Foundation
import Network
let params = NWParameters.tcp
let framer = STARTTLSFramer.options()
params.defaultProtocolStack.applicationProtocols = [framer]
let connection = NWConnection(
host: .ipv4(IPv4Address("127.0.0.1")!), port: .init(integerLiteral: 8089), using: params)
connection.stateUpdateHandler = { newState in
print("connection newState \(newState)")
}
connection.start(queue: .main)
RunLoop.main.run()
// STARTLSFramer.swift
import Foundation
import Network
final class STARTTLSFramer: NWProtocolFramerImplementation {
static let label: String = "STARTTLSFramer"
init(framer: NWProtocolFramer.Instance) {}
func handleOutput(
framer instance: NWProtocolFramer.Instance, message: NWProtocolFramer.Message,
messageLength: Int, isComplete: Bool
) {
fatalError()
}
func wakeup(framer instance: NWProtocolFramer.Instance) {
fatalError()
}
func stop(framer instance: NWProtocolFramer.Instance) -> Bool { true }
func cleanup(framer instance: NWProtocolFramer.Instance) {}
func start(framer instance: NWProtocolFramer.Instance) -> NWProtocolFramer.StartResult {
instance.writeOutput(data: Data("hello\n".utf8))
return .willMarkReady
}
private var accumulated = Data()
func doUpgrade(instance: NWProtocolFramer.Instance) {
let tlsOptions = NWProtocolTLS.Options()
sec_protocol_options_set_min_tls_protocol_version(tlsOptions.securityProtocolOptions, .TLSv12)
// load identity
let secIdentity = createSecIdentity()!
let identity = sec_identity_create(secIdentity)
sec_protocol_options_set_local_identity(tlsOptions.securityProtocolOptions, identity!)
try! instance.prependApplicationProtocol(options: tlsOptions)
instance.passThroughOutput()
instance.passThroughInput()
instance.markReady()
}
func handleInput(framer instance: NWProtocolFramer.Instance) -> Int {
repeat {
let success = instance.parseInput(minimumIncompleteLength: 1, maximumLength: 2048) {
buffer, _ in
let count = buffer?.count ?? 0
if let buffer {
accumulated.append(contentsOf: buffer)
}
return count
}
if !success { break }
} while true
// some validation
self.accumulated.removeAll()
self.doUpgrade(instance: instance)
return 0
}
static func options() -> NWProtocolFramer.Options {
let startTLSDef = NWProtocolFramer.Definition(implementation: STARTTLSFramer.self)
let result = NWProtocolFramer.Options(definition: startTLSDef)
return result
}
}