diff --git a/src/build/spawn_stream.mjs b/src/build/spawn_stream.mjs index 1aa7f81321..199583daf0 100644 --- a/src/build/spawn_stream.mjs +++ b/src/build/spawn_stream.mjs @@ -23,6 +23,7 @@ export const spawnStream = (command, ...parameters) => const stdout = []; const stderr = []; + console.debug(`Running [${[command, ...parameters.map(e => `'${e}'`)].join(' ')}]`); const childProcess = spawn(command, parameters, {env: process.env}); const forEachMessageLine = (buffer, callback) => { diff --git a/src/cordova/apple/OutlineAppleLib/Package.swift b/src/cordova/apple/OutlineAppleLib/Package.swift index a4645b6841..d8937405fc 100644 --- a/src/cordova/apple/OutlineAppleLib/Package.swift +++ b/src/cordova/apple/OutlineAppleLib/Package.swift @@ -8,10 +8,13 @@ let package = Package( products: [ .library( name: "OutlineAppleLib", - targets: ["Tun2socks", "OutlineTunnel", "PacketTunnelProvider"]), + targets: ["Tun2socks", "OutlineTunnel"]), + .library( + name: "PacketTunnelProvider", + targets: ["PacketTunnelProvider"]), ], dependencies: [ - .package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack.git", exact: "3.7.4"), + .package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack.git", from: "3.7.4"), ], targets: [ .target( @@ -29,7 +32,9 @@ let package = Package( ), .target( name: "OutlineTunnel", - dependencies: [], + dependencies: [ + .product(name: "CocoaLumberjackSwift", package: "CocoaLumberjack"), + ], path: "Sources/OutlineTunnelSources" ), .binaryTarget( diff --git a/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/OutlineTunnel.swift b/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/OutlineTunnel.swift index 4198edbe70..1cb7baf4b1 100644 --- a/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/OutlineTunnel.swift +++ b/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/OutlineTunnel.swift @@ -13,6 +13,9 @@ // limitations under the License. import Foundation +import NetworkExtension + +import CocoaLumberjackSwift // Serializable class to wrap a tunnel's configuration. // Properties must be kept in sync with ServerConfig in www/types/outlinePlugin.d.ts @@ -20,47 +23,148 @@ import Foundation // target of the OutlineAppleLib Swift Package. @objcMembers public class OutlineTunnel: NSObject, Codable { - public var id: String? - public var host: String? - public var port: String? - public var method: String? - public var password: String? - public var prefix: Data? - public var config: [String: String] { - let scalars = prefix?.map{Unicode.Scalar($0)} - let characters = scalars?.map{Character($0)} - let prefixStr = String(characters ?? []) - return ["host": host ?? "", "port": port ?? "", "password": password ?? "", - "method": method ?? "", "prefix": prefixStr] - } + public var id: String? + public var host: String? + public var port: String? + public var method: String? + public var password: String? + public var prefix: Data? + public var config: [String: String] { + let scalars = prefix?.map{Unicode.Scalar($0)} + let characters = scalars?.map{Character($0)} + let prefixStr = String(characters ?? []) + return ["host": host ?? "", "port": port ?? "", "password": password ?? "", + "method": method ?? "", "prefix": prefixStr] + } + + @objc + public enum TunnelStatus: Int { + case connected = 0 + case disconnected = 1 + case reconnecting = 2 + } + + public convenience init(id: String, config: [String: Any]) { + self.init() + self.id = id + self.host = config["host"] as? String + self.password = config["password"] as? String + self.method = config["method"] as? String + if let port = config["port"] { + self.port = String(describing: port) // Handle numeric values + } + if let prefix = config["prefix"] as? String { + self.prefix = Data(prefix.utf16.map{UInt8($0)}) + } + } + + public func encode() -> Data? { + return try? JSONEncoder().encode(self) + } + + public static func decode(_ jsonData: Data) -> OutlineTunnel? { + return try? JSONDecoder().decode(OutlineTunnel.self, from: jsonData) + } + + // Helper function that we can call from Objective-C. + @objc public static func getTunnelNetworkSettings(tunnelRemoteAddress: String) -> NEPacketTunnelNetworkSettings { + // The remote address is not used for routing, but for display in Settings > VPN > Outline. + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: tunnelRemoteAddress) + + // Configure VPN address and routing. + let vpnAddress = selectVpnAddress(interfaceAddresses: getNetworkInterfaceAddresses()) + let ipv4Settings = NEIPv4Settings(addresses: [vpnAddress], subnetMasks: ["255.255.255.0"]) + ipv4Settings.includedRoutes = [NEIPv4Route.default()] + ipv4Settings.excludedRoutes = getExcludedIpv4Routes() + settings.ipv4Settings = ipv4Settings - @objc - public enum TunnelStatus: Int { - case connected = 0 - case disconnected = 1 - case reconnecting = 2 - } + // Configure with Cloudflare, Quad9, and OpenDNS resolver addresses. + settings.dnsSettings = NEDNSSettings(servers: ["1.1.1.1", "9.9.9.9", "208.67.222.222", "208.67.220.220"]) + + return settings + } +} - public convenience init(id: String, config: [String: Any]) { - self.init() - self.id = id - self.host = config["host"] as? String - self.password = config["password"] as? String - self.method = config["method"] as? String - if let port = config["port"] { - self.port = String(describing: port) // Handle numeric values +// Returns all IPv4 addresses of all interfaces. +func getNetworkInterfaceAddresses() -> [String] { + var interfaces: UnsafeMutablePointer? + var addresses = [String]() + + guard getifaddrs(&interfaces) == 0 else { + DDLogError("Failed to retrieve network interface addresses") + return addresses } - if let prefix = config["prefix"] as? String { - self.prefix = Data(prefix.utf16.map{UInt8($0)}) + + var interface = interfaces + while interface != nil { + // Only consider IPv4 interfaces. + if interface!.pointee.ifa_addr.pointee.sa_family == UInt8(AF_INET) { + let addr = interface!.pointee.ifa_addr!.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee.sin_addr } + if let ip = String(cString: inet_ntoa(addr), encoding: .utf8) { + addresses.append(ip) + } + } + interface = interface!.pointee.ifa_next } - } + + freeifaddrs(interfaces) + + return addresses +} + +let kVpnSubnetCandidates: [String: String] = [ + "10": "10.111.222.0", + "172": "172.16.9.1", + "192": "192.168.20.1", + "169": "169.254.19.0" +] - public func encode() -> Data? { - return try? JSONEncoder().encode(self) - } +// Given the list of known interface addresses, returns a local network IP address to use for the VPN. +func selectVpnAddress(interfaceAddresses: [String]) -> String { + var candidates = kVpnSubnetCandidates + + for address in interfaceAddresses { + for subnetPrefix in kVpnSubnetCandidates.keys { + if address.hasPrefix(subnetPrefix) { + // The subnet (not necessarily the address) is in use, remove it from our list. + candidates.removeValue(forKey: subnetPrefix) + } + } + } + guard !candidates.isEmpty else { + // Even though there is an interface bound to the subnet candidates, the collision probability + // with an actual address is low. + return kVpnSubnetCandidates.randomElement()!.value + } + // Select a random subnet from the remaining candidates. + return candidates.randomElement()!.value +} - public static func decode(_ jsonData: Data) -> OutlineTunnel? { - return try? JSONDecoder().decode(OutlineTunnel.self, from: jsonData) - } +let kExcludedSubnets = [ + "10.0.0.0/8", + "100.64.0.0/10", + "169.254.0.0/16", + "172.16.0.0/12", + "192.0.0.0/24", + "192.0.2.0/24", + "192.31.196.0/24", + "192.52.193.0/24", + "192.88.99.0/24", + "192.168.0.0/16", + "192.175.48.0/24", + "198.18.0.0/15", + "198.51.100.0/24", + "203.0.113.0/24", + "240.0.0.0/4" +] +func getExcludedIpv4Routes() -> [NEIPv4Route] { + var excludedIpv4Routes = [NEIPv4Route]() + for cidrSubnet in kExcludedSubnets { + if let subnet = Subnet.parse(cidrSubnet) { + let route = NEIPv4Route(destinationAddress: subnet.address, subnetMask: subnet.mask) + excludedIpv4Routes.append(route) + } + } + return excludedIpv4Routes } diff --git a/src/cordova/plugin/apple/src/OutlineVpn.swift b/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/OutlineVpn.swift similarity index 88% rename from src/cordova/plugin/apple/src/OutlineVpn.swift rename to src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/OutlineVpn.swift index b8a651c239..04f3825179 100644 --- a/src/cordova/plugin/apple/src/OutlineVpn.swift +++ b/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/OutlineVpn.swift @@ -12,24 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -import CocoaLumberjack import CocoaLumberjackSwift import NetworkExtension -import OutlineTunnel +import Tun2socks // Manages the system's VPN tunnel through the VpnExtension process. @objcMembers -class OutlineVpn: NSObject { - static let shared = OutlineVpn() +public class OutlineVpn: NSObject { + public static let shared = OutlineVpn() private static let kVpnExtensionBundleId = "\(Bundle.main.bundleIdentifier!).VpnExtension" - typealias Callback = (ErrorCode) -> Void - typealias VpnStatusObserver = (NEVPNStatus, String) -> Void + public typealias Callback = (ErrorCode) -> Void + public typealias VpnStatusObserver = (NEVPNStatus, String) -> Void public private(set) var activeTunnelId: String? private var tunnelManager: NETunnelProviderManager? private var vpnStatusObserver: VpnStatusObserver? - private let connectivity: OutlineConnectivity private enum Action { static let start = "start" @@ -70,7 +68,6 @@ class OutlineVpn: NSObject { } override private init() { - connectivity = OutlineConnectivity() super.init() getTunnelManager() { manager in guard manager != nil else { @@ -87,28 +84,25 @@ class OutlineVpn: NSObject { // MARK: Interface // Starts a VPN tunnel as specified in the OutlineTunnel object. - func start(_ tunnel: OutlineTunnel, _ completion: @escaping (Callback)) { - guard let tunnelId = tunnel.id else { - DDLogError("Missing tunnel ID") - return completion(ErrorCode.illegalServerConfiguration) - } - if isActive(tunnelId) { + public func start(_ tunnelId: String, configJson: [String: Any], _ completion: @escaping (Callback)) { + guard !isActive(tunnelId) else { return completion(ErrorCode.noError) - } else if isVpnConnected() { - return restartVpn(tunnelId, config: tunnel.config, completion: completion) } - self.startVpn(tunnel, isAutoConnect: false, completion) + if isVpnConnected() { + return restartVpn(tunnelId, configJson: configJson, completion: completion) + } + self.startVpn(tunnelId, configJson: configJson, isAutoConnect: false, completion) } // Starts the last successful VPN tunnel. - func startLastSuccessfulTunnel(_ completion: @escaping (Callback)) { + @objc public func startLastSuccessfulTunnel(_ completion: @escaping (Callback)) { // Explicitly pass an empty tunnel's configuration, so the VpnExtension process retrieves // the last configuration from disk. - self.startVpn(OutlineTunnel(), isAutoConnect: true, completion) + self.startVpn(nil, configJson:nil, isAutoConnect: true, completion) } // Tears down the VPN if the tunnel with id |tunnelId| is active. - func stop(_ tunnelId: String) { + public func stop(_ tunnelId: String) { if !isActive(tunnelId) { return DDLogWarn("Cannot stop VPN, tunnel ID \(tunnelId)") } @@ -116,7 +110,7 @@ class OutlineVpn: NSObject { } // Determines whether a server is reachable via TCP. - func isServerReachable(host: String, port: UInt16, _ completion: @escaping Callback) { + public func isServerReachable(host: String, port: UInt16, _ completion: @escaping Callback) { if isVpnConnected() { // All the device's traffic, including the Outline app, go through the VpnExtension process. // Performing a reachability test, opening a TCP socket to a host/port, will succeed @@ -132,19 +126,20 @@ class OutlineVpn: NSObject { completion(ErrorCode(rawValue: rawCode) ?? ErrorCode.serverUnreachable) } } else { - connectivity.isServerReachable(host: host, port: port) { isReachable in + DispatchQueue.global(qos: .background).async { + let isReachable = ShadowsocksCheckServerReachable(host, Int(port), nil) completion(isReachable ? ErrorCode.noError : ErrorCode.serverUnreachable) } } } // Calls |observer| when the VPN's status changes. - func onVpnStatusChange(_ observer: @escaping(VpnStatusObserver)) { + public func onVpnStatusChange(_ observer: @escaping(VpnStatusObserver)) { vpnStatusObserver = observer } // Returns whether |tunnelId| is actively proxying through the VPN. - func isActive(_ tunnelId: String?) -> Bool { + public func isActive(_ tunnelId: String?) -> Bool { if self.activeTunnelId == nil { return false } @@ -153,9 +148,7 @@ class OutlineVpn: NSObject { // MARK: Helpers - private func startVpn( - _ tunnel: OutlineTunnel, isAutoConnect: Bool, _ completion: @escaping(Callback)) { - let tunnelId = tunnel.id + private func startVpn(_ tunnelId: String?, configJson: [String: Any]?, isAutoConnect: Bool, _ completion: @escaping(Callback)) { setupVpn() { error in if error != nil { DDLogError("Failed to setup VPN: \(String(describing: error))") @@ -165,17 +158,18 @@ class OutlineVpn: NSObject { self.sendVpnExtensionMessage(message) { response in self.onStartVpnExtensionMessage(response, completion: completion) } - var config: [String: String]? = nil + var tunnelOptions: [String: Any]? = nil if !isAutoConnect { - config = tunnel.config - config?[MessageKey.tunnelId] = tunnelId + // TODO(fortuna): put this in a subkey + tunnelOptions = configJson + tunnelOptions?[MessageKey.tunnelId] = tunnelId } else { // macOS app was started by launcher. - config = [MessageKey.isOnDemand: "true"]; + tunnelOptions = [MessageKey.isOnDemand: "true"]; } let session = self.tunnelManager?.connection as! NETunnelProviderSession do { - try session.startTunnel(options: config) + try session.startTunnel(options: tunnelOptions) } catch let error as NSError { DDLogError("Failed to start VPN: \(error)") completion(ErrorCode.vpnStartFailure) @@ -191,13 +185,13 @@ class OutlineVpn: NSObject { } // Sends message to extension to restart the tunnel without tearing down the VPN. - private func restartVpn(_ tunnelId: String, config: [String: String], + private func restartVpn(_ tunnelId: String, configJson: [String: Any], completion: @escaping(Callback)) { if activeTunnelId != nil { vpnStatusObserver?(.disconnected, activeTunnelId!) } let message = [MessageKey.action: Action.restart, MessageKey.tunnelId: tunnelId, - MessageKey.config:config] as [String : Any] + MessageKey.config: configJson] as [String : Any] self.sendVpnExtensionMessage(message) { response in self.onStartVpnExtensionMessage(response, completion: completion) } @@ -310,7 +304,7 @@ class OutlineVpn: NSObject { // Receives NEVPNStatusDidChange notifications. Calls onTunnelStatusChange for the active // tunnel. - @objc func vpnStatusChanged() { + func vpnStatusChanged() { if let vpnStatus = tunnelManager?.connection.status { if let tunnelId = activeTunnelId { if (vpnStatus == .disconnected) { diff --git a/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/Subnet.swift b/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/Subnet.swift index 2ba3990709..30bbfb8921 100644 --- a/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/Subnet.swift +++ b/src/cordova/apple/OutlineAppleLib/Sources/OutlineTunnelSources/Subnet.swift @@ -19,24 +19,6 @@ import Foundation // target of the OutlineAppleLib Swift Package. @objcMembers public class Subnet: NSObject { - public static let kReservedSubnets = [ - "10.0.0.0/8", - "100.64.0.0/10", - "169.254.0.0/16", - "172.16.0.0/12", - "192.0.0.0/24", - "192.0.2.0/24", - "192.31.196.0/24", - "192.52.193.0/24", - "192.88.99.0/24", - "192.168.0.0/16", - "192.175.48.0/24", - "198.18.0.0/15", - "198.51.100.0/24", - "203.0.113.0/24", - "240.0.0.0/4" - ] - // Parses a CIDR subnet into a Subnet object. Returns nil on failure. public static func parse(_ cidrSubnet: String) -> Subnet? { let components = cidrSubnet.components(separatedBy: "/") @@ -51,17 +33,6 @@ public class Subnet: NSObject { return Subnet(address: components[0], prefix: prefix) } - // Returns a list of reserved Subnets. - public static func getReservedSubnets() -> [Subnet] { - var subnets: [Subnet] = [] - for cidrSubnet in kReservedSubnets { - if let subnet = self.parse(cidrSubnet) { - subnets.append(subnet) - } - } - return subnets - } - public var address: String public var prefix: UInt16 public var mask: String diff --git a/src/cordova/apple/OutlineAppleLib/Sources/PacketTunnelProviderSources/PacketTunnelProvider.m b/src/cordova/apple/OutlineAppleLib/Sources/PacketTunnelProviderSources/PacketTunnelProvider.m index 872e37cad7..00ded12b74 100644 --- a/src/cordova/apple/OutlineAppleLib/Sources/PacketTunnelProviderSources/PacketTunnelProvider.m +++ b/src/cordova/apple/OutlineAppleLib/Sources/PacketTunnelProviderSources/PacketTunnelProvider.m @@ -34,7 +34,6 @@ NSString *const kMessageKeyPort = @"port"; NSString *const kMessageKeyOnDemand = @"is-on-demand"; NSString *const kDefaultPathKey = @"defaultPath"; -static NSDictionary *kVpnSubnetCandidates; // Subnets to bind the VPN. @interface PacketTunnelProvider () @property (nonatomic) NSString *hostNetworkAddress; // IP address of the host in the active network. @@ -74,12 +73,6 @@ - (id)init { [DDLog addLogger:_fileLogger]; _tunnelStore = [[OutlineTunnelStore alloc] initWithAppGroup:appGroup]; - kVpnSubnetCandidates = @{ - @"10" : @"10.111.222.0", - @"172" : @"172.16.9.1", - @"192" : @"192.168.20.1", - @"169" : @"169.254.19.0" - }; _packetQueue = dispatch_queue_create("org.outline.ios.packetqueue", DISPATCH_QUEUE_SERIAL); @@ -147,7 +140,7 @@ - (void)startTunnelWithOptions:(NSDictionary *)options userInfo:nil]); } - [self connectTunnel:[self getTunnelNetworkSettings] + [self connectTunnel:[OutlineTunnel getTunnelNetworkSettingsWithTunnelRemoteAddress:self.hostNetworkAddress] completion:^(NSError *_Nullable error) { if (error != nil) { [self execAppCallbackForAction:kActionStart errorCode:vpnPermissionNotGranted]; @@ -204,7 +197,7 @@ - (void)handleAppMessage:(NSData *)messageData completionHandler:(void (^)(NSDat } DDLogInfo(@"Received app message: %@", action); void (^callbackWrapper)(NSNumber *) = ^void(NSNumber *errorCode) { - NSString *tunnelId; + NSString *tunnelId = @""; if (self.tunnelConfig != nil) { tunnelId = self.tunnelConfig.id; } @@ -300,33 +293,6 @@ - (void)connectTunnel:(NEPacketTunnelNetworkSettings *)settings }]; } -- (NEPacketTunnelNetworkSettings *) getTunnelNetworkSettings { - NSString *vpnAddress = [self selectVpnAddress]; - NEIPv4Settings *ipv4Settings = [[NEIPv4Settings alloc] initWithAddresses:@[ vpnAddress ] - subnetMasks:@[ @"255.255.255.0" ]]; - ipv4Settings.includedRoutes = @[[NEIPv4Route defaultRoute]]; - ipv4Settings.excludedRoutes = [self getExcludedIpv4Routes]; - - // The remote address is not used for routing, but for display in Settings > VPN > Outline. - NEPacketTunnelNetworkSettings *settings = - [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:self.hostNetworkAddress]; - settings.IPv4Settings = ipv4Settings; - // Configure with Cloudflare, Quad9, and OpenDNS resolver addresses. - settings.DNSSettings = [[NEDNSSettings alloc] - initWithServers:@[ @"1.1.1.1", @"9.9.9.9", @"208.67.222.222", @"208.67.220.220" ]]; - return settings; -} - -- (NSArray *)getExcludedIpv4Routes { - NSMutableArray *excludedIpv4Routes = [[NSMutableArray alloc] init]; - for (Subnet *subnet in [Subnet getReservedSubnets]) { - NEIPv4Route *route = [[NEIPv4Route alloc] initWithDestinationAddress:subnet.address - subnetMask:subnet.mask]; - [excludedIpv4Routes addObject:route]; - } - return excludedIpv4Routes; -} - // Registers KVO for the `defaultPath` property to receive network connectivity changes. - (void)listenForNetworkChanges { [self stopListeningForNetworkChanges]; @@ -435,54 +401,6 @@ - (NSString *)getNetworkIpAddress:(const char *)ipv4Str { return [NSString stringWithUTF8String:networkAddress]; } -- (NSArray *)getNetworkInterfaceAddresses { - struct ifaddrs *interfaces = nil; - NSMutableArray *addresses = [NSMutableArray new]; - if (getifaddrs(&interfaces) != 0) { - DDLogError(@"Failed to retrieve network interface addresses"); - return addresses; - } - struct ifaddrs *interface = interfaces; - while (interface != nil) { - if (interface->ifa_addr->sa_family == AF_INET) { - // Only consider IPv4 interfaces. - NSString *address = [NSString - stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)interface->ifa_addr)->sin_addr)]; - [addresses addObject:address]; - } - interface = interface->ifa_next; - } - freeifaddrs(interfaces); - - return addresses; -} - -// Selects an IPv4 address for the VPN to bind to from a pool of private subnets by checking against -// the subnets assigned to the existing network interfaces. -- (NSString *)selectVpnAddress { - NSMutableDictionary *candidates = - [[NSMutableDictionary alloc] initWithDictionary:kVpnSubnetCandidates]; - for (NSString *address in [self getNetworkInterfaceAddresses]) { - for (NSString *subnetPrefix in kVpnSubnetCandidates) { - if ([address hasPrefix:subnetPrefix]) { - // The subnet (not necessarily the address) is in use, remove it from our list. - [candidates removeObjectForKey:subnetPrefix]; - } - } - } - if (candidates.count == 0) { - // Even though there is an interface bound to the subnet candidates, the collision probability - // with an actual address is low. - return [self selectRandomValueFromDictionary:kVpnSubnetCandidates]; - } - // Select a random subnet from the remaining candidates. - return [self selectRandomValueFromDictionary:candidates]; -} - -- (id)selectRandomValueFromDictionary:(NSDictionary *)dict { - return [dict.allValues objectAtIndex:(arc4random_uniform((uint32_t)dict.count))]; -} - #pragma mark - tun2socks // Restarts tun2socks if |configChanged| or the host's IP address has changed in the network. @@ -501,7 +419,7 @@ - (void)reconnectTunnel:(bool)configChanged { } if (!configChanged && [activeHostNetworkAddress isEqualToString:self.hostNetworkAddress]) { // Nothing changed. Connect the tunnel with the current settings. - [self connectTunnel:[self getTunnelNetworkSettings] + [self connectTunnel:[OutlineTunnel getTunnelNetworkSettingsWithTunnelRemoteAddress:self.hostNetworkAddress] completion:^(NSError *_Nullable error) { if (error != nil) { [self cancelTunnelWithError:error]; @@ -539,7 +457,7 @@ - (void)reconnectTunnel:(bool)configChanged { userInfo:nil]]; return; } - [self connectTunnel:[self getTunnelNetworkSettings] + [self connectTunnel:[OutlineTunnel getTunnelNetworkSettingsWithTunnelRemoteAddress:self.hostNetworkAddress] completion:^(NSError *_Nullable error) { if (error != nil) { [self execAppCallbackForAction:kActionStart errorCode:vpnStartFailure]; diff --git a/src/cordova/apple/OutlineAppleLib/Tests/OutlineTunnelTest/OutlineTunnelTest.swift b/src/cordova/apple/OutlineAppleLib/Tests/OutlineTunnelTest/OutlineTunnelTest.swift index a2a55013c0..344b277553 100644 --- a/src/cordova/apple/OutlineAppleLib/Tests/OutlineTunnelTest/OutlineTunnelTest.swift +++ b/src/cordova/apple/OutlineAppleLib/Tests/OutlineTunnelTest/OutlineTunnelTest.swift @@ -1,4 +1,8 @@ import XCTest + +import NetworkExtension +import Tun2socks + @testable import OutlineTunnel @testable import PacketTunnelProvider @@ -11,4 +15,34 @@ final class OutlineTunnelTest: XCTestCase { let tunnelStore = OutlineTunnelStore(appGroup: OutlineTunnelTest.kAppGroup) XCTAssertNil(tunnelStore.load()) } + + func testSelectVpnAddress() { + XCTAssertEqual("10.111.222.0", selectVpnAddress(interfaceAddresses:["172.16.9.2", "192.168.20.2", "169.254.19.1"])) + XCTAssertEqual("172.16.9.1", selectVpnAddress(interfaceAddresses:["10.111.222.1", "192.168.20.2", "169.254.19.1"])) + XCTAssertEqual("192.168.20.1", selectVpnAddress(interfaceAddresses:["10.111.222.1", "172.16.9.2", "169.254.19.1"])) + XCTAssertEqual("169.254.19.0", selectVpnAddress(interfaceAddresses:["10.111.222.1", "172.16.9.2", "192.168.20.2"])) + XCTAssertTrue(kVpnSubnetCandidates.values.contains(selectVpnAddress(interfaceAddresses: getNetworkInterfaceAddresses()))) + } + + func testGetTunnelNetworkSettings() { + let settings = OutlineTunnel.getTunnelNetworkSettings(tunnelRemoteAddress: "1.2.3.4") + + XCTAssertEqual("1.2.3.4", settings.tunnelRemoteAddress) + + XCTAssertEqual(1, settings.ipv4Settings?.addresses.count) + XCTAssertTrue(kVpnSubnetCandidates.values.contains(settings.ipv4Settings?.addresses[0] ?? "")) + XCTAssertEqual(["255.255.255.0"], settings.ipv4Settings?.subnetMasks) + + XCTAssertEqual([NEIPv4Route.default()], settings.ipv4Settings?.includedRoutes) + XCTAssertEqual(15, settings.ipv4Settings?.excludedRoutes?.count ?? 0) + + XCTAssertEqual(["1.1.1.1", "9.9.9.9", "208.67.222.222", "208.67.220.220"], settings.dnsSettings?.servers) + } + + func testReachability() { + // TODO(fortuna): run a local server instead. + XCTAssertTrue(ShadowsocksCheckServerReachable("8.8.8.8", 853, nil)) + XCTAssertTrue(ShadowsocksCheckServerReachable("google.com", 443, nil)) + XCTAssertFalse(ShadowsocksCheckServerReachable("nonexistent.getoutline.org", 443, nil)) + } } diff --git a/src/cordova/apple/ios.xcworkspace/xcshareddata/swiftpm/Package.resolved b/src/cordova/apple/ios.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0e64201720..0bd028c28b 100644 --- a/src/cordova/apple/ios.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/src/cordova/apple/ios.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/CocoaLumberjack/CocoaLumberjack", "state" : { - "revision" : "80ada1f753b0d53d9b57c465936a7c4169375002", - "version" : "3.7.4" + "revision" : "0188d31089b5881a269e01777be74c7316924346", + "version" : "3.8.0" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa", "state" : { - "revision" : "c3c19e29f775ee95b77aa3168f3e2fd6c20deba6", - "version" : "7.31.3" + "revision" : "cf43eac1aa12017868c257ad3854ad87a5de0758", + "version" : "7.31.5" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log.git", "state" : { - "revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c", - "version" : "1.4.4" + "revision" : "32e8d724467f8fe623624570367e3d50c5638e46", + "version" : "1.5.2" } } ], diff --git a/src/cordova/apple/macos.xcworkspace/xcshareddata/swiftpm/Package.resolved b/src/cordova/apple/macos.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0e64201720..0bd028c28b 100644 --- a/src/cordova/apple/macos.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/src/cordova/apple/macos.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/CocoaLumberjack/CocoaLumberjack", "state" : { - "revision" : "80ada1f753b0d53d9b57c465936a7c4169375002", - "version" : "3.7.4" + "revision" : "0188d31089b5881a269e01777be74c7316924346", + "version" : "3.8.0" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa", "state" : { - "revision" : "c3c19e29f775ee95b77aa3168f3e2fd6c20deba6", - "version" : "7.31.3" + "revision" : "cf43eac1aa12017868c257ad3854ad87a5de0758", + "version" : "7.31.5" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log.git", "state" : { - "revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c", - "version" : "1.4.4" + "revision" : "32e8d724467f8fe623624570367e3d50c5638e46", + "version" : "1.5.2" } } ], diff --git a/src/cordova/apple/xcode/ios/Outline.xcodeproj/project.pbxproj b/src/cordova/apple/xcode/ios/Outline.xcodeproj/project.pbxproj index 39160bc5d6..00b350b451 100755 --- a/src/cordova/apple/xcode/ios/Outline.xcodeproj/project.pbxproj +++ b/src/cordova/apple/xcode/ios/Outline.xcodeproj/project.pbxproj @@ -17,17 +17,13 @@ 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 302D95EF14D2391D003F00A1 /* MainViewController.m */; }; 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 302D95F014D2391D003F00A1 /* MainViewController.xib */; }; 3B0347531F212F0200C8EF1F /* VpnExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 3B0347481F212F0100C8EF1F /* VpnExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 3B0347611F212F6900C8EF1F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 941052A220F54953928FF2E2 /* libz.tbd */; }; 52CBB892295BD8F200D0073F /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB891295BD8F200D0073F /* CocoaLumberjack */; }; 52CBB894295BD8F200D0073F /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB893295BD8F200D0073F /* CocoaLumberjackSwift */; }; - 52CBB896295BD93600D0073F /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB895295BD93600D0073F /* CocoaLumberjack */; }; 52CE53E7295B6A310064D03D /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 52CE53E6295B6A310064D03D /* Sentry */; }; + 52E783062A5880CF00355E64 /* PacketTunnelProvider in Frameworks */ = {isa = PBXBuildFile; productRef = 52E783052A5880CF00355E64 /* PacketTunnelProvider */; }; 5F7F90AE0E924FD7B065C415 /* CDVStatusBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 0394302BA6114B2AB648D4FF /* CDVStatusBar.m */; }; 6AFF5BF91D6E424B00AB3073 /* CDVLaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6AFF5BF81D6E424B00AB3073 /* CDVLaunchScreen.storyboard */; }; - 7213383E07114591931284F8 /* OutlineConnectivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 119806AF98394D7D8749BB30 /* OutlineConnectivity.swift */; }; F63DC2182970AFFA00D92E0A /* OutlineAppleLib in Frameworks */ = {isa = PBXBuildFile; productRef = F63DC2172970AFFA00D92E0A /* OutlineAppleLib */; }; - F63DC21A2970B00100D92E0A /* OutlineAppleLib in Frameworks */ = {isa = PBXBuildFile; productRef = F63DC2192970B00100D92E0A /* OutlineAppleLib */; }; - FC28C299201BB4E9005FD743 /* OutlineVpn.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC28C297201BB4E9005FD743 /* OutlineVpn.swift */; }; FC8C31091FAA8032004262BE /* OutlineSentryLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC8C31081FAA8032004262BE /* OutlineSentryLogger.swift */; }; FC8C310B1FAA814A004262BE /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8C310A1FAA814A004262BE /* NetworkExtension.framework */; }; FC8C310C1FAA88FB004262BE /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8C310A1FAA814A004262BE /* NetworkExtension.framework */; }; @@ -62,13 +58,6 @@ remoteGlobalIDString = C0C01EB21E3911D50056E6CB; remoteInfo = Cordova; }; - FC28C2C1202913BE005FD743 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = D2AAC07D0554694100DB518D; - remoteInfo = CordovaLib; - }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -99,7 +88,6 @@ 0168F53D3AFF46F5B346C874 /* CDVStatusBar.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = CDVStatusBar.h; path = "cordova-plugin-statusbar/CDVStatusBar.h"; sourceTree = ""; }; 0207DA571B56EA530066E2B4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Outline/Images.xcassets; sourceTree = SOURCE_ROOT; }; 0394302BA6114B2AB648D4FF /* CDVStatusBar.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = CDVStatusBar.m; path = "cordova-plugin-statusbar/CDVStatusBar.m"; sourceTree = ""; }; - 119806AF98394D7D8749BB30 /* OutlineConnectivity.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = OutlineConnectivity.swift; path = "cordova-plugin-outline/OutlineConnectivity.swift"; sourceTree = ""; }; 1D3623240D0F684500981E51 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 1D3623250D0F684500981E51 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 1D6058910D05DD3D006BFB54 /* Outline.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Outline.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -127,7 +115,6 @@ ED33DF2A687741AEAF9F8254 /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; F63DC2162970AFE600D92E0A /* OutlineAppleLib */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OutlineAppleLib; path = ../../src/cordova/apple/OutlineAppleLib; sourceTree = ""; }; F840E1F0165FE0F500CFE078 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = Outline/config.xml; sourceTree = ""; }; - FC28C297201BB4E9005FD743 /* OutlineVpn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OutlineVpn.swift; path = "cordova-plugin-outline/OutlineVpn.swift"; sourceTree = ""; }; FC55AB411F4F960A0056F12C /* VpnExtension-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "VpnExtension-Info.plist"; path = "Outline/VpnExtension-Info.plist"; sourceTree = SOURCE_ROOT; }; FC8C31081FAA8032004262BE /* OutlineSentryLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OutlineSentryLogger.swift; path = "cordova-plugin-outline/OutlineSentryLogger.swift"; sourceTree = ""; }; FC8C310A1FAA814A004262BE /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; @@ -152,10 +139,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F63DC21A2970B00100D92E0A /* OutlineAppleLib in Frameworks */, FC8C310B1FAA814A004262BE /* NetworkExtension.framework in Frameworks */, - 3B0347611F212F6900C8EF1F /* libz.tbd in Frameworks */, - 52CBB896295BD93600D0073F /* CocoaLumberjack in Frameworks */, + 52E783062A5880CF00355E64 /* PacketTunnelProvider in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -259,12 +244,10 @@ 307C750510C5A3420062BCA9 /* Plugins */ = { isa = PBXGroup; children = ( - FC28C297201BB4E9005FD743 /* OutlineVpn.swift */, FC8C31081FAA8032004262BE /* OutlineSentryLogger.swift */, 0394302BA6114B2AB648D4FF /* CDVStatusBar.m */, 0168F53D3AFF46F5B346C874 /* CDVStatusBar.h */, AAFAFA54943F490EAF4CD5BC /* OutlinePlugin.swift */, - 119806AF98394D7D8749BB30 /* OutlineConnectivity.swift */, 91E45572BB494E9299D2DD41 /* CDVClipboard.m */, 936C2951B7544BC8A20B6746 /* CDVClipboard.h */, ); @@ -341,12 +324,10 @@ buildRules = ( ); dependencies = ( - FC28C2C2202913BE005FD743 /* PBXTargetDependency */, ); name = VpnExtension; packageProductDependencies = ( - 52CBB895295BD93600D0073F /* CocoaLumberjack */, - F63DC2192970B00100D92E0A /* OutlineAppleLib */, + 52E783052A5880CF00355E64 /* PacketTunnelProvider */, ); productName = VpnExtension; productReference = 3B0347481F212F0100C8EF1F /* VpnExtension.appex */; @@ -482,14 +463,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - FC28C299201BB4E9005FD743 /* OutlineVpn.swift in Sources */, 1D60589B0D05DD56006BFB54 /* main.m in Sources */, 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */, 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */, 5F7F90AE0E924FD7B065C415 /* CDVStatusBar.m in Sources */, 2A617D29B96942E58B082FAC /* OutlinePlugin.swift in Sources */, FC8C31091FAA8032004262BE /* OutlineSentryLogger.swift in Sources */, - 7213383E07114591931284F8 /* OutlineConnectivity.swift in Sources */, 1273B4E700B84E31B2528701 /* CDVClipboard.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -514,11 +493,6 @@ target = 3B0347471F212F0100C8EF1F /* VpnExtension */; targetProxy = 3B0347511F212F0200C8EF1F /* PBXContainerItemProxy */; }; - FC28C2C2202913BE005FD743 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = CordovaLib; - targetProxy = FC28C2C1202913BE005FD743 /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -835,16 +809,16 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/CocoaLumberjack/CocoaLumberjack"; requirement = { - kind = exactVersion; - version = 3.7.4; + kind = upToNextMajorVersion; + minimumVersion = 3.7.4; }; }; 52CE53E5295B6A310064D03D /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/getsentry/sentry-cocoa"; requirement = { - kind = exactVersion; - version = 7.31.3; + kind = upToNextMajorVersion; + minimumVersion = 7.31.3; }; }; /* End XCRemoteSwiftPackageReference section */ @@ -860,21 +834,16 @@ package = 52CBB890295BD8F200D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; productName = CocoaLumberjackSwift; }; - 52CBB895295BD93600D0073F /* CocoaLumberjack */ = { - isa = XCSwiftPackageProductDependency; - package = 52CBB890295BD8F200D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; - productName = CocoaLumberjack; - }; 52CE53E6295B6A310064D03D /* Sentry */ = { isa = XCSwiftPackageProductDependency; package = 52CE53E5295B6A310064D03D /* XCRemoteSwiftPackageReference "sentry-cocoa" */; productName = Sentry; }; - F63DC2172970AFFA00D92E0A /* OutlineAppleLib */ = { + 52E783052A5880CF00355E64 /* PacketTunnelProvider */ = { isa = XCSwiftPackageProductDependency; - productName = OutlineAppleLib; + productName = PacketTunnelProvider; }; - F63DC2192970B00100D92E0A /* OutlineAppleLib */ = { + F63DC2172970AFFA00D92E0A /* OutlineAppleLib */ = { isa = XCSwiftPackageProductDependency; productName = OutlineAppleLib; }; diff --git a/src/cordova/apple/xcode/ios/Outline.xcodeproj/xcshareddata/xcschemes/VpnExtension.xcscheme b/src/cordova/apple/xcode/ios/Outline.xcodeproj/xcshareddata/xcschemes/VpnExtension.xcscheme index 113c9e91bc..53d149b593 100644 --- a/src/cordova/apple/xcode/ios/Outline.xcodeproj/xcshareddata/xcschemes/VpnExtension.xcscheme +++ b/src/cordova/apple/xcode/ios/Outline.xcodeproj/xcshareddata/xcschemes/VpnExtension.xcscheme @@ -21,30 +21,13 @@ ReferencedContainer = "container:Outline.xcodeproj"> - - - - - - - - + + - - - - - - - + - + diff --git a/src/cordova/apple/xcode/ios/Outline/VpnExtension-Info.plist b/src/cordova/apple/xcode/ios/Outline/VpnExtension-Info.plist index 687db25667..5d3ca566bb 100644 --- a/src/cordova/apple/xcode/ios/Outline/VpnExtension-Info.plist +++ b/src/cordova/apple/xcode/ios/Outline/VpnExtension-Info.plist @@ -22,8 +22,6 @@ $(PRODUCT_NAME) CFBundlePackageType XPC! - CFBundleVersion - 39 NSExtension NSExtensionPointIdentifier diff --git a/src/cordova/apple/xcode/macos/Outline.xcodeproj/project.pbxproj b/src/cordova/apple/xcode/macos/Outline.xcodeproj/project.pbxproj index 8cb346560e..5ba0a6202a 100644 --- a/src/cordova/apple/xcode/macos/Outline.xcodeproj/project.pbxproj +++ b/src/cordova/apple/xcode/macos/Outline.xcodeproj/project.pbxproj @@ -8,13 +8,9 @@ /* Begin PBXBuildFile section */ 52355CFB295B70030057557B /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 52355CFA295B70030057557B /* Sentry */; }; + 52C198972A587525006E0ACE /* PacketTunnelProvider in Frameworks */ = {isa = PBXBuildFile; productRef = 52C198962A587525006E0ACE /* PacketTunnelProvider */; }; 52CBB849295BC79300D0073F /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB848295BC79300D0073F /* CocoaLumberjack */; }; 52CBB84B295BC79300D0073F /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB84A295BC79300D0073F /* CocoaLumberjackSwift */; }; - 52CBB84D295BC7F700D0073F /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB84C295BC7F700D0073F /* CocoaLumberjack */; }; - 52CBB84F295BC7F700D0073F /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB84E295BC7F700D0073F /* CocoaLumberjackSwift */; }; - 52CBB851295BC81900D0073F /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB850295BC81900D0073F /* CocoaLumberjack */; }; - 52CBB853295BC81900D0073F /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 52CBB852295BC81900D0073F /* CocoaLumberjackSwift */; }; - 591E137A57874E4A92300F1B /* OutlineConnectivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C0FE24369C408597E6E257 /* OutlineConnectivity.swift */; }; 707060AF18FFC05700755D46 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 707060AE18FFC05700755D46 /* MainViewController.xib */; }; 70BD682718FFB02D00A1EFCF /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70BD682618FFB02D00A1EFCF /* Cocoa.framework */; }; 70BD683118FFB02D00A1EFCF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 70BD682F18FFB02D00A1EFCF /* InfoPlist.strings */; }; @@ -29,13 +25,11 @@ DBBA975361C94599B9DD3891 /* CDVClipboard.m in Sources */ = {isa = PBXBuildFile; fileRef = 469466FF1BDB44C081741BF5 /* CDVClipboard.m */; }; EAD11960A27C45AFA5E46584 /* Outline-Bridging-Header.h in Resources */ = {isa = PBXBuildFile; fileRef = A508014170A1492DB0D800B3 /* Outline-Bridging-Header.h */; }; F63DC2202970B26500D92E0A /* OutlineAppleLib in Frameworks */ = {isa = PBXBuildFile; productRef = F63DC21F2970B26500D92E0A /* OutlineAppleLib */; }; - F63DC2222970B27300D92E0A /* OutlineAppleLib in Frameworks */ = {isa = PBXBuildFile; productRef = F63DC2212970B27300D92E0A /* OutlineAppleLib */; }; F63DC2242970B27A00D92E0A /* OutlineAppleLib in Frameworks */ = {isa = PBXBuildFile; productRef = F63DC2232970B27A00D92E0A /* OutlineAppleLib */; }; FC0799E92037532A00A1C822 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC0799E82037532A00A1C822 /* AppDelegate.swift */; }; FC0799F02037532A00A1C822 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FC0799EE2037532A00A1C822 /* Main.storyboard */; }; FC0799F72037543F00A1C822 /* OutlineLauncher.app in Copy OutlineLauncher */ = {isa = PBXBuildFile; fileRef = FC0799E62037532900A1C822 /* OutlineLauncher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; FC0799F92037545D00A1C822 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC0799F82037545D00A1C822 /* ServiceManagement.framework */; }; - FC28C281201A93BF005FD743 /* OutlineVpn.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC28C280201A93BF005FD743 /* OutlineVpn.swift */; }; FC5FF93A1F3E1E5F0032A745 /* VpnExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = FC5FF92B1F3E1E5F0032A745 /* VpnExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; FC5FF9471F3E1E8B0032A745 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC5FF9461F3E1E8B0032A745 /* NetworkExtension.framework */; }; FC5FF9501F3E1FD40032A745 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC5FF9461F3E1E8B0032A745 /* NetworkExtension.framework */; }; @@ -45,6 +39,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 5296DB072A57BBAF0009C9BB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 70BD681B18FFB02D00A1EFCF /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC0799E52037532900A1C822; + remoteInfo = OutlineLauncher; + }; 70BD685F18FFB04300A1EFCF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 70BD685A18FFB04300A1EFCF /* CordovaLib.xcodeproj */; @@ -94,7 +95,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 01C0FE24369C408597E6E257 /* OutlineConnectivity.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = OutlineConnectivity.swift; path = "cordova-plugin-outline/OutlineConnectivity.swift"; sourceTree = ""; }; 19209C3AABCC45AB8CFDD974 /* CDVClipboard.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = CDVClipboard.h; path = "cordova-plugin-clipboard/CDVClipboard.h"; sourceTree = ""; }; 469466FF1BDB44C081741BF5 /* CDVClipboard.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = CDVClipboard.m; path = "cordova-plugin-clipboard/CDVClipboard.m"; sourceTree = ""; }; 707060AE18FFC05700755D46 /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = ""; }; @@ -126,7 +126,6 @@ FC0799F12037532A00A1C822 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; FC0799F22037532A00A1C822 /* OutlineLauncher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OutlineLauncher.entitlements; sourceTree = ""; }; FC0799F82037545D00A1C822 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; }; - FC28C280201A93BF005FD743 /* OutlineVpn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OutlineVpn.swift; path = "cordova-plugin-outline/OutlineVpn.swift"; sourceTree = ""; }; FC55AB411F4F960A0056F12C /* VpnExtension-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "VpnExtension-Info.plist"; path = "Outline/VpnExtension-Info.plist"; sourceTree = SOURCE_ROOT; }; FC5FF9121F3E1D1B0032A745 /* Outline.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Outline.entitlements; sourceTree = ""; }; FC5FF92B1F3E1E5F0032A745 /* VpnExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = VpnExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -159,8 +158,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 52CBB851295BC81900D0073F /* CocoaLumberjack in Frameworks */, - 52CBB853295BC81900D0073F /* CocoaLumberjackSwift in Frameworks */, F63DC2242970B27A00D92E0A /* OutlineAppleLib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -169,10 +166,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F63DC2222970B27300D92E0A /* OutlineAppleLib in Frameworks */, FC5FF9471F3E1E8B0032A745 /* NetworkExtension.framework in Frameworks */, - 52CBB84D295BC7F700D0073F /* CocoaLumberjack in Frameworks */, - 52CBB84F295BC7F700D0073F /* CocoaLumberjackSwift in Frameworks */, + 52C198972A587525006E0ACE /* PacketTunnelProvider in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -182,10 +177,8 @@ 7069FBC31905A1F9000B48E1 /* Plugins */ = { isa = PBXGroup; children = ( - FC28C280201A93BF005FD743 /* OutlineVpn.swift */, AA09EA80E0C54DFFB24A1810 /* OutlinePlugin.swift */, FC8C30FB1FA7DDCB004262BE /* OutlineSentryLogger.swift */, - 01C0FE24369C408597E6E257 /* OutlineConnectivity.swift */, 469466FF1BDB44C081741BF5 /* CDVClipboard.m */, 19209C3AABCC45AB8CFDD974 /* CDVClipboard.h */, ); @@ -335,6 +328,7 @@ ); dependencies = ( 70BD687118FFB19500A1EFCF /* PBXTargetDependency */, + 5296DB082A57BBAF0009C9BB /* PBXTargetDependency */, FC5FF9391F3E1E5F0032A745 /* PBXTargetDependency */, ); name = Outline; @@ -362,8 +356,6 @@ ); name = OutlineLauncher; packageProductDependencies = ( - 52CBB850295BC81900D0073F /* CocoaLumberjack */, - 52CBB852295BC81900D0073F /* CocoaLumberjackSwift */, F63DC2232970B27A00D92E0A /* OutlineAppleLib */, ); productName = OutlineLauncher; @@ -384,9 +376,7 @@ ); name = VpnExtension; packageProductDependencies = ( - 52CBB84C295BC7F700D0073F /* CocoaLumberjack */, - 52CBB84E295BC7F700D0073F /* CocoaLumberjackSwift */, - F63DC2212970B27300D92E0A /* OutlineAppleLib */, + 52C198962A587525006E0ACE /* PacketTunnelProvider */, ); productName = VpnExtension; productReference = FC5FF92B1F3E1E5F0032A745 /* VpnExtension.appex */; @@ -546,10 +536,8 @@ FC6E7F8E204DC1BE003CB365 /* CDVMacOsUrlHandler.swift in Sources */, 70BD686E18FFB0BF00A1EFCF /* MainViewController.m in Sources */, 737FCE3329B34151A67331B4 /* OutlinePlugin.swift in Sources */, - 591E137A57874E4A92300F1B /* OutlineConnectivity.swift in Sources */, FC7D56051F86969E00ABD5CA /* EventMonitor.swift in Sources */, DBBA975361C94599B9DD3891 /* CDVClipboard.m in Sources */, - FC28C281201A93BF005FD743 /* OutlineVpn.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -571,6 +559,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 5296DB082A57BBAF0009C9BB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC0799E52037532900A1C822 /* OutlineLauncher */; + targetProxy = 5296DB072A57BBAF0009C9BB /* PBXContainerItemProxy */; + }; 70BD687118FFB19500A1EFCF /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = CordovaLib; @@ -998,16 +991,16 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/getsentry/sentry-cocoa"; requirement = { - kind = exactVersion; - version = 7.31.3; + kind = upToNextMajorVersion; + minimumVersion = 7.31.3; }; }; 52CBB847295BC79300D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/CocoaLumberjack/CocoaLumberjack"; requirement = { - kind = exactVersion; - version = 3.7.4; + kind = upToNextMajorVersion; + minimumVersion = 3.7.4; }; }; /* End XCRemoteSwiftPackageReference section */ @@ -1018,32 +1011,16 @@ package = 52355CF9295B70020057557B /* XCRemoteSwiftPackageReference "sentry-cocoa" */; productName = Sentry; }; - 52CBB848295BC79300D0073F /* CocoaLumberjack */ = { - isa = XCSwiftPackageProductDependency; - package = 52CBB847295BC79300D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; - productName = CocoaLumberjack; - }; - 52CBB84A295BC79300D0073F /* CocoaLumberjackSwift */ = { + 52C198962A587525006E0ACE /* PacketTunnelProvider */ = { isa = XCSwiftPackageProductDependency; - package = 52CBB847295BC79300D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; - productName = CocoaLumberjackSwift; + productName = PacketTunnelProvider; }; - 52CBB84C295BC7F700D0073F /* CocoaLumberjack */ = { - isa = XCSwiftPackageProductDependency; - package = 52CBB847295BC79300D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; - productName = CocoaLumberjack; - }; - 52CBB84E295BC7F700D0073F /* CocoaLumberjackSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 52CBB847295BC79300D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; - productName = CocoaLumberjackSwift; - }; - 52CBB850295BC81900D0073F /* CocoaLumberjack */ = { + 52CBB848295BC79300D0073F /* CocoaLumberjack */ = { isa = XCSwiftPackageProductDependency; package = 52CBB847295BC79300D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; productName = CocoaLumberjack; }; - 52CBB852295BC81900D0073F /* CocoaLumberjackSwift */ = { + 52CBB84A295BC79300D0073F /* CocoaLumberjackSwift */ = { isa = XCSwiftPackageProductDependency; package = 52CBB847295BC79300D0073F /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; productName = CocoaLumberjackSwift; @@ -1052,10 +1029,6 @@ isa = XCSwiftPackageProductDependency; productName = OutlineAppleLib; }; - F63DC2212970B27300D92E0A /* OutlineAppleLib */ = { - isa = XCSwiftPackageProductDependency; - productName = OutlineAppleLib; - }; F63DC2232970B27A00D92E0A /* OutlineAppleLib */ = { isa = XCSwiftPackageProductDependency; productName = OutlineAppleLib; diff --git a/src/cordova/apple/xcode/macos/Outline.xcodeproj/xcshareddata/xcschemes/VpnExtension.xcscheme b/src/cordova/apple/xcode/macos/Outline.xcodeproj/xcshareddata/xcschemes/VpnExtension.xcscheme index 338185b98c..05b46257a8 100644 --- a/src/cordova/apple/xcode/macos/Outline.xcodeproj/xcshareddata/xcschemes/VpnExtension.xcscheme +++ b/src/cordova/apple/xcode/macos/Outline.xcodeproj/xcshareddata/xcschemes/VpnExtension.xcscheme @@ -21,30 +21,13 @@ ReferencedContainer = "container:Outline.xcodeproj"> - - - - - - - - + + - - - - - - - + - + diff --git a/src/cordova/apple/xcode/macos/Outline/Classes/AppDelegate.m b/src/cordova/apple/xcode/macos/Outline/Classes/AppDelegate.m index fea3f075cc..6c681b5f9d 100644 --- a/src/cordova/apple/xcode/macos/Outline/Classes/AppDelegate.m +++ b/src/cordova/apple/xcode/macos/Outline/Classes/AppDelegate.m @@ -16,6 +16,7 @@ #import "Outline-Swift.h" @import ServiceManagement; +@import OutlineTunnel; @interface AppDelegate() @property (strong, nonatomic) NSStatusItem *statusItem; diff --git a/src/cordova/apple/xcode/macos/Outline/VpnExtension-Info.plist b/src/cordova/apple/xcode/macos/Outline/VpnExtension-Info.plist index 494f6577fd..18e4ad4bcf 100644 --- a/src/cordova/apple/xcode/macos/Outline/VpnExtension-Info.plist +++ b/src/cordova/apple/xcode/macos/Outline/VpnExtension-Info.plist @@ -22,8 +22,6 @@ $(PRODUCT_NAME) CFBundlePackageType XPC! - CFBundleVersion - 40 LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSExtension diff --git a/src/cordova/plugin/apple/src/OutlineConnectivity.swift b/src/cordova/plugin/apple/src/OutlineConnectivity.swift deleted file mode 100644 index a5881bfa11..0000000000 --- a/src/cordova/plugin/apple/src/OutlineConnectivity.swift +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2018 The Outline Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import CocoaLumberjack -import CocoaLumberjackSwift -import Tun2socks - -// Class to perform connectivity tests against remote Outline servers. -class OutlineConnectivity: NSObject { - - // Asynchronously calls |completion| with a boolean representing whether |host| and |port| - // are reachable. - func isServerReachable(host: String, port: UInt16, completion: @escaping((Bool) -> Void)) { - DispatchQueue.global(qos: .background).async { - guard let networkIp = self.getNetworkIpAddress(host) else { - DDLogError("Failed to retrieve the remote host IP address in the network"); - return completion(false) - } - completion(ShadowsocksCheckServerReachable(networkIp, Int(port), nil)) - } - } - - // Calls getaddrinfo to retrieve the IP address literal as a string for |ipv4Address| in the - // active network. This is necessary to support IPv6 DNS64/NAT64 networks. For more details see: - // https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html - private func getNetworkIpAddress(_ ipv4Address: String) -> String? { - var hints = addrinfo( - ai_flags: AI_DEFAULT, - ai_family: AF_UNSPEC, - ai_socktype: SOCK_STREAM, - ai_protocol: 0, - ai_addrlen: 0, - ai_canonname: nil, - ai_addr: nil, - ai_next: nil) - var info: UnsafeMutablePointer? - let err = getaddrinfo(ipv4Address, nil, &hints, &info) - if err != 0 { - DDLogError("getaddrinfo failed \(String(describing: gai_strerror(err)))") - return nil - } - defer { - freeaddrinfo(info) - } - return getIpAddressString(addr: info?.pointee.ai_addr) - } - - private func getIpAddressString(addr: UnsafePointer?) -> String? { - guard addr != nil else { - DDLogError("Failed to get IP address string: invalid argument") - return nil - } - var host : String? - var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST)) - let err = getnameinfo(addr, socklen_t(addr!.pointee.sa_len), &buffer, socklen_t(buffer.count), - nil, 0, NI_NUMERICHOST | NI_NUMERICSERV) - if err == 0 { - host = String(cString: buffer) - } - return host - } -} diff --git a/src/cordova/plugin/apple/src/OutlinePlugin.swift b/src/cordova/plugin/apple/src/OutlinePlugin.swift index 386bfbf5a7..4b8ea2df53 100644 --- a/src/cordova/plugin/apple/src/OutlinePlugin.swift +++ b/src/cordova/plugin/apple/src/OutlinePlugin.swift @@ -76,12 +76,12 @@ class OutlinePlugin: CDVPlugin { errorCode: OutlineVpn.ErrorCode.illegalServerConfiguration) } DDLogInfo("\(Action.start) \(tunnelId)") - guard let config = command.argument(at: 1) as? [String: Any], containsExpectedKeys(config) else { + // TODO(fortuna): Move the config validation to the config parsing code in Go. + guard let configJson = command.argument(at: 1) as? [String: Any], containsExpectedKeys(configJson) else { return sendError("Invalid configuration", callbackId: command.callbackId, errorCode: OutlineVpn.ErrorCode.illegalServerConfiguration) } - let tunnel = OutlineTunnel(id: tunnelId, config: config) - OutlineVpn.shared.start(tunnel) { errorCode in + OutlineVpn.shared.start(tunnelId, configJson:configJson) { errorCode in if errorCode == OutlineVpn.ErrorCode.noError { #if os(macOS) NotificationCenter.default.post( @@ -234,9 +234,9 @@ class OutlinePlugin: CDVPlugin { } // Returns whether |config| contains all the expected keys - private func containsExpectedKeys(_ config: [String: Any]?) -> Bool { - return config?["host"] != nil && config?["port"] != nil && - config?["password"] != nil && config?["method"] != nil + private func containsExpectedKeys(_ configJson: [String: Any]?) -> Bool { + return configJson?["host"] != nil && configJson?["port"] != nil && + configJson?["password"] != nil && configJson?["method"] != nil } // MARK: Callback helpers diff --git a/src/cordova/plugin/plugin.xml b/src/cordova/plugin/plugin.xml index 234f230b85..02c0f23d32 100644 --- a/src/cordova/plugin/plugin.xml +++ b/src/cordova/plugin/plugin.xml @@ -80,9 +80,7 @@ - - @@ -95,9 +93,7 @@ - - diff --git a/src/cordova/setup.action.mjs b/src/cordova/setup.action.mjs index 06fc3383ad..ac6ff7560e 100644 --- a/src/cordova/setup.action.mjs +++ b/src/cordova/setup.action.mjs @@ -72,7 +72,7 @@ export async function main(...parameters) { } async function androidDebug(verbose) { - return cordova.prepare({ + await cordova.prepare({ platforms: ['android'], save: false, verbose, @@ -85,8 +85,6 @@ async function makeReplacements(replacements) { for (const replacement of replacements) { results = [...results, ...(await replace(replacement))]; } - - return Promise.resolve(results); } async function androidRelease(versionName, buildNumber, verbose) { @@ -99,7 +97,7 @@ async function androidRelease(versionName, buildNumber, verbose) { const manifestXmlGlob = path.join(getRootDir(), 'platforms', 'android', '**', 'AndroidManifest.xml'); const configXmlGlob = path.join(getRootDir(), 'platforms', 'android', '**', 'config.xml'); - return makeReplacements([ + await makeReplacements([ { files: manifestXmlGlob, from: ['android:versionName="1.0"', 'android:versionName="0.0.0-debug"'], @@ -135,7 +133,7 @@ async function appleIosDebug(verbose) { }); // TODO(daniellacosse): move this to a cordova hook - return spawnStream('rsync', '-avc', 'src/cordova/apple/xcode/ios/', 'platforms/ios/'); + await spawnStream('rsync', '-avc', 'src/cordova/apple/xcode/ios/', 'platforms/ios/'); } async function appleMacOsDebug(verbose) { @@ -156,11 +154,11 @@ async function appleMacOsDebug(verbose) { }); // TODO(daniellacosse): move this to a cordova hook - return spawnStream('rsync', '-avc', 'src/cordova/apple/xcode/macos/', 'platforms/osx/'); + await spawnStream('rsync', '-avc', 'src/cordova/apple/xcode/macos/', 'platforms/osx/'); } -function setAppleVersion(platform, versionName, buildNumber) { - return makeReplacements([ +async function setAppleVersion(platform, versionName, buildNumber) { + await makeReplacements([ { files: `platforms/${platform}/Outline/*.plist`, from: /CFBundleShortVersionString<\/key>\s*.*<\/string>/g, @@ -188,7 +186,7 @@ async function appleIosRelease(version, buildNumber, verbose) { // TODO(daniellacosse): move this to a cordova hook await spawnStream('rsync', '-avc', 'src/cordova/apple/xcode/ios/', 'platforms/ios/'); - return setAppleVersion('ios', version, buildNumber); + await setAppleVersion('ios', version, buildNumber); } async function appleMacOsRelease(version, buildNumber, verbose) { @@ -207,7 +205,7 @@ async function appleMacOsRelease(version, buildNumber, verbose) { // TODO(daniellacosse): move this to a cordova hook await spawnStream('rsync', '-avc', 'src/cordova/apple/xcode/macos/', 'platforms/osx/'); - return setAppleVersion('osx', version, buildNumber); + await setAppleVersion('osx', version, buildNumber); } if (import.meta.url === url.pathToFileURL(process.argv[1]).href) { diff --git a/src/cordova/test.action.mjs b/src/cordova/test.action.mjs index 8a1b9cb04d..4b520dc4df 100644 --- a/src/cordova/test.action.mjs +++ b/src/cordova/test.action.mjs @@ -51,7 +51,7 @@ export async function main(...parameters) { 'clean', 'test', '-scheme', - APPLE_LIBRARY_NAME, + `${APPLE_LIBRARY_NAME}-Package`, '-destination', outlinePlatform === 'macos' ? `platform=macOS,arch=${os.machine()}`