//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// An NWParameters object contains the parameters necessary to create /// a network connection or listener. NWParameters include any preferences for /// network paths (such as required, prohibited, and preferred networks, and local /// endpoint requirements); preferences for data transfer and quality of service; /// and the protocols to be used for a connection along with any protocol-specific /// options. // NOTE: older overlays had Network.NWParameters as the ObjC name. // The two must coexist, so it was renamed. The old name must not be // used in the new runtime. _TtC7Network13_NWParameters is the // mangled name for Network._NWParameters. @_objcRuntimeName(_TtC7Network13_NWParameters) @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) public final class NWParameters : CustomDebugStringConvertible { public var debugDescription: String { return String("\(self.nw)") } internal let nw : nw_parameters_t /// Creates a parameters object that is configured for TLS and TCP. The caller can use /// the default configuration for TLS and TCP, or set specific options for each protocol, /// or disable TLS. /// /// - Parameter tls: TLS options or nil for no TLS /// - Parameter tcp: TCP options. Defaults to NWProtocolTCP.Options() with no options overridden. /// - Returns: NWParameters object that can be used for creating a connection or listener public convenience init(tls: NWProtocolTLS.Options?, tcp: NWProtocolTCP.Options = NWProtocolTCP.Options()) { self.init() let protocolStack = self.defaultProtocolStack protocolStack.transportProtocol = tcp if let tls = tls { protocolStack.applicationProtocols = [tls] } else { protocolStack.applicationProtocols = [] } } /// Creates a parameters object that is configured for DTLS and UDP. The caller can use /// the default configuration for DTLS and UDP, or set specific options for each protocol, /// or disable TLS. /// /// - Parameter dtls: DTLS options or nil for no DTLS /// - Parameter udp: UDP options. Defaults to NWProtocolUDP.Options() with no options overridden. /// - Returns: NWParameters object that can be used for create a connection or listener public convenience init(dtls: NWProtocolTLS.Options?, udp: NWProtocolUDP.Options = NWProtocolUDP.Options()) { self.init() let protocolStack = self.defaultProtocolStack protocolStack.transportProtocol = udp if let dtls = dtls { protocolStack.applicationProtocols = [dtls] } else { protocolStack.applicationProtocols = [] } } /// Creates a generic NWParameters object. Note that in order to use parameters /// with a NWConnection or a NetworkListener, the parameters must have protocols /// added into the defaultProtocolStack. Clients using standard protocol /// configurations should use init(tls:tcp:) or init(dtls:udp:). public init() { self.nw = nw_parameters_create() } private init(nw: nw_parameters_t) { self.nw = nw } /// Default set of parameters for TLS over TCP /// This is equivalent to calling init(tls:NWProtocolTLS.Options(), tcp:NWProtocolTCP.Options()) public class var tls: NWParameters { return NWParameters(tls:NWProtocolTLS.Options()) } /// Default set of parameters for DTLS over UDP /// This is equivalent to calling init(dtls:NWProtocolTLS.Options(), udp:NWProtocolUDP.Options()) public class var dtls: NWParameters { return NWParameters(dtls:NWProtocolTLS.Options()) } /// Default set of parameters for TCP /// This is equivalent to calling init(tls:nil, tcp:NWProtocolTCP.Options()) public class var tcp: NWParameters { return NWParameters(tls:nil) } /// Default set of parameters for UDP /// This is equivalent to calling init(dtls:nil, udp:NWProtocolUDP.Options()) public class var udp: NWParameters { return NWParameters(dtls:nil) } /// Require a connection to use a specific interface, or fail if not available public var requiredInterface: NWInterface? { set { if let interface = newValue { nw_parameters_require_interface(self.nw, interface.nw) } else { nw_parameters_require_interface(self.nw, nil) } } get { if let interface = nw_parameters_copy_required_interface(self.nw) { return NWInterface(interface) } else { return nil } } } /// Require a connection to use a specific interface type, or fail if not available public var requiredInterfaceType: NWInterface.InterfaceType { set { nw_parameters_set_required_interface_type(self.nw, newValue.nw) } get { return NWInterface.InterfaceType(nw_parameters_get_required_interface_type(self.nw)) } } /// Define one or more interfaces that a connection will not be allowed to use public var prohibitedInterfaces: [NWInterface]? { set { nw_parameters_clear_prohibited_interfaces(self.nw) if let prohibitedInterfaces = newValue { for prohibitedInterface in prohibitedInterfaces { nw_parameters_prohibit_interface(self.nw, prohibitedInterface.nw) } } } get { var interfaces = [NWInterface]() nw_parameters_iterate_prohibited_interfaces(self.nw) { (interface) in interfaces.append(NWInterface(interface)) return true } return interfaces } } /// Define one or more interface types that a connection will not be allowed to use public var prohibitedInterfaceTypes: [NWInterface.InterfaceType]? { set { nw_parameters_clear_prohibited_interface_types(self.nw) if let prohibitedInterfaceTypes = newValue { for prohibitedInterfaceType in prohibitedInterfaceTypes { nw_parameters_prohibit_interface_type(self.nw, prohibitedInterfaceType.nw) } } } get { var interfaceTypes = [NWInterface.InterfaceType]() nw_parameters_iterate_prohibited_interface_types(self.nw) { (interfaceType) in interfaceTypes.append(NWInterface.InterfaceType(interfaceType)) return true } return interfaceTypes } } /// Disallow connection from using interfaces considered expensive public var prohibitExpensivePaths: Bool { set { nw_parameters_set_prohibit_expensive(self.nw, newValue) } get { return nw_parameters_get_prohibit_expensive(self.nw) } } /// If true, a direct connection will be attempted first even if proxies are configured. If the direct connection /// fails, connecting through the proxies will still be attempted. public var preferNoProxies: Bool { set { nw_parameters_set_prefer_no_proxy(self.nw, newValue) } get { return nw_parameters_get_prefer_no_proxy(self.nw) } } /// Force a specific local address to be used. This value is nil by /// default, in which case the system selects the most appropriate /// local address and selects a local port. public var requiredLocalEndpoint: NWEndpoint? { set { if let endpoint = newValue { nw_parameters_set_local_endpoint(self.nw, endpoint.nw) } else { nw_parameters_set_local_endpoint(self.nw, nil) } } get { if let endpoint = nw_parameters_copy_local_endpoint(self.nw) { return NWEndpoint(endpoint) } else { return nil } } } /// Allow multiple connections to use the same local address and port /// (SO_REUSEADDR and SO_REUSEPORT). public var allowLocalEndpointReuse: Bool { set { nw_parameters_set_reuse_local_address(self.nw, newValue) } get { return nw_parameters_get_reuse_local_address(self.nw) } } /// Cause an NWListener to only advertise services on the local link, /// and only accept connections from the local link. public var acceptLocalOnly: Bool { set { nw_parameters_set_local_only(self.nw, newValue) } get { return nw_parameters_get_local_only(self.nw) } } /// Allow the inclusion of peer-to-peer interfaces when /// listening or establishing outbound connections. This parameter /// will not take effect if a specific interface is required. /// This parameter is applicable when advertising a Bonjour service /// on a listener, or connecting to a Bonjour service. public var includePeerToPeer: Bool { set { nw_parameters_set_include_peer_to_peer(self.nw, newValue) } get { return nw_parameters_get_include_peer_to_peer(self.nw) } } /// The ServiceClass represents the network queuing priority to use /// for traffic generated by a NWConnection. public enum ServiceClass { /// Default priority traffic case bestEffort /// Bulk traffic, or traffic that can be de-prioritized behind foreground traffic case background /// Interactive video traffic case interactiveVideo /// Interactive voice traffic case interactiveVoice /// Responsive data case responsiveData /// Signaling case signaling internal var nw: nw_service_class_t { switch self { case .bestEffort: return Network.nw_service_class_best_effort case .background: return Network.nw_service_class_background case .interactiveVideo: return Network.nw_service_class_interactive_video case .interactiveVoice: return Network.nw_service_class_interactive_voice case .responsiveData: return Network.nw_service_class_responsive_data case .signaling: return Network.nw_service_class_signaling } } internal init(_ nw: nw_service_class_t) { switch nw { case Network.nw_service_class_best_effort: self = .bestEffort case Network.nw_service_class_background: self = .background case Network.nw_service_class_interactive_video: self = .interactiveVideo case Network.nw_service_class_interactive_voice: self = .interactiveVoice case Network.nw_service_class_responsive_data: self = .responsiveData case Network.nw_service_class_signaling: self = .signaling default: self = .bestEffort } } } public var serviceClass: NWParameters.ServiceClass { set { nw_parameters_set_service_class(self.nw, newValue.nw) } get { return NWParameters.ServiceClass(nw_parameters_get_service_class(self.nw)) } } /// Multipath services represent the modes of multipath usage that are /// allowed for connections. public enum MultipathServiceType { /// No multipath transport will be attempted case disabled /// Only use the expensive interface when the when the primary one is not available case handover /// Use the expensive interface more aggressively to reduce latency case interactive /// Use all available interfaces to provide the highest throughput and lowest latency case aggregate internal var nw: nw_multipath_service_t { switch self { case .disabled: return Network.nw_multipath_service_disabled case .handover: return Network.nw_multipath_service_handover case .interactive: return Network.nw_multipath_service_interactive case .aggregate: return Network.nw_multipath_service_aggregate } } internal init(_ nw: nw_multipath_service_t) { switch nw { case Network.nw_multipath_service_disabled: self = .disabled case Network.nw_multipath_service_handover: self = .handover case Network.nw_multipath_service_interactive: self = .interactive case Network.nw_multipath_service_aggregate: self = .aggregate default: self = .disabled } } } public var multipathServiceType: NWParameters.MultipathServiceType { set { nw_parameters_set_multipath_service(self.nw, newValue.nw) } get { return NWParameters.MultipathServiceType(nw_parameters_get_multipath_service(self.nw)) } } /// Use fast open for an outbound NWConnection, which may be done at any /// protocol level. Use of fast open requires that the caller send /// idempotent data on the connection before the connection may move /// into ready state. As a side effect, this may implicitly enable /// fast open for protocols in the stack, even if they did not have /// fast open explicitly enabled on them (such as the option to enable /// TCP Fast Open). public var allowFastOpen: Bool { set { nw_parameters_set_fast_open_enabled(self.nw, newValue) } get { return nw_parameters_get_fast_open_enabled(self.nw) } } /// Allow or prohibit the use of expired DNS answers during connection establishment. /// If allowed, a DNS answer that was previously returned may be re-used for new /// connections even after the answers are considered expired. A query for fresh answers /// will be sent in parallel, and the fresh answers will be used as alternate addresses /// in case the expired answers do not result in successful connections. /// By default, this value is .systemDefault, which allows the system to determine /// if it is appropriate to use expired answers. public enum ExpiredDNSBehavior { /// Let the system determine whether or not to allow expired DNS answers case systemDefault /// Explicitly allow the use of expired DNS answers case allow /// Explicitly prohibit the use of expired DNS answers case prohibit internal var nw: nw_parameters_expired_dns_behavior_t { switch self { case .systemDefault: return Network.nw_parameters_expired_dns_behavior_default case .allow: return Network.nw_parameters_expired_dns_behavior_allow case .prohibit: return Network.nw_parameters_expired_dns_behavior_prohibit } } internal init(_ nw: nw_parameters_expired_dns_behavior_t) { switch nw { case Network.nw_parameters_expired_dns_behavior_default: self = .systemDefault case Network.nw_parameters_expired_dns_behavior_allow: self = .allow case Network.nw_parameters_expired_dns_behavior_prohibit: self = .prohibit default: self = .systemDefault } } } public var expiredDNSBehavior: NWParameters.ExpiredDNSBehavior { set { nw_parameters_set_expired_dns_behavior(self.nw, newValue.nw) } get { return NWParameters.ExpiredDNSBehavior(nw_parameters_get_expired_dns_behavior(self.nw)) } } /// A ProtocolStack contains a list of protocols to use for a connection. /// The members of the protocol stack are NWProtocolOptions objects, each /// defining which protocol to use within the stack along with any protocol-specific /// options. Each stack includes an array of application-level protocols, a single /// transport-level protocol, and an optional internet-level protocol. If the internet- /// level protocol is not specified, any available and applicable IP address family /// may be used. // Set the ObjC name of this class to be nested in the customized ObjC // name of NWParameters. @_objcRuntimeName(_TtCC7Network13_NWParameters13ProtocolStack) public class ProtocolStack { public var applicationProtocols: [NWProtocolOptions] { set { nw_protocol_stack_clear_application_protocols(self.nw) for applicationProtocol in newValue.reversed() { nw_protocol_stack_prepend_application_protocol(self.nw, applicationProtocol.nw) } } get { var applicationProtocols = [NWProtocolOptions]() nw_protocol_stack_iterate_application_protocols(self.nw) { (protocolOptions) in let applicationDefinition = nw_protocol_options_copy_definition(protocolOptions) if (nw_protocol_definition_is_equal(applicationDefinition, NWProtocolTLS.definition.nw)) { applicationProtocols.append(NWProtocolTLS.Options(protocolOptions)) } } return applicationProtocols } } public var transportProtocol: NWProtocolOptions? { set { if let transport = newValue { nw_protocol_stack_set_transport_protocol(self.nw, transport.nw) } } get { if let transport = nw_protocol_stack_copy_transport_protocol(nw) { let transportDefinition = nw_protocol_options_copy_definition(transport) if (nw_protocol_definition_is_equal(transportDefinition, NWProtocolTCP.definition.nw)) { return NWProtocolTCP.Options(transport) } else if (nw_protocol_definition_is_equal(transportDefinition, NWProtocolUDP.definition.nw)) { return NWProtocolUDP.Options(transport) } } return nil } } public var internetProtocol: NWProtocolOptions? { set { // Not currently allowed return } get { if let ip = nw_protocol_stack_copy_internet_protocol(nw) { if (nw_protocol_definition_is_equal(nw_protocol_options_copy_definition(ip), NWProtocolIP.definition.nw)) { return NWProtocolIP.Options(ip) } } return nil } } internal let nw: nw_protocol_stack_t internal init(_ nw: nw_protocol_stack_t) { self.nw = nw } } /// Every NWParameters has a default protocol stack, although it may start out empty. public var defaultProtocolStack: NWParameters.ProtocolStack { get { return NWParameters.ProtocolStack(nw_parameters_copy_default_protocol_stack(self.nw)) } } /// Perform a deep copy of parameters public func copy() -> NWParameters { return NWParameters(nw: nw_parameters_copy(self.nw)) } }